Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified .DS_Store
Binary file not shown.
132 changes: 122 additions & 10 deletions .github/workflows/react-native-cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ env:
UNIT_SENTRY_DSN: ${{ secrets.UNIT_SENTRY_DSN }}
UNIT_ANDROID_KS: ${{ secrets.UNIT_ANDROID_KS }}
UNIT_GOOGLE_SERVICES: ${{ secrets.UNIT_GOOGLE_SERVICES }}
UNIT_IOS_GOOGLE_SERVICES: ${{ secrets.UNIT_IOS_GOOGLE_SERVICES }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
APP_STORE_CONNECT_KEY_ID: ${{ secrets.APP_STORE_CONNECT_KEY_ID }}
APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
Expand All @@ -71,6 +72,8 @@ env:
UNIT_APP_KEY: ${{ secrets.UNIT_APP_KEY }}
APP_KEY: ${{ secrets.APP_KEY }}
NODE_OPTIONS: --openssl-legacy-provider
CHANGERAWR_API_KEY: ${{ secrets.CHANGERAWR_API_KEY }}
CHANGERAWR_API_URL: ${{ secrets.CHANGERAWR_API_URL }}

jobs:
check-skip:
Expand Down Expand Up @@ -153,6 +156,11 @@ jobs:
run: |
echo $UNIT_GOOGLE_SERVICES | base64 -d > google-services.json

- name: 📋 Create Google Json File for iOS
if: ${{ matrix.platform == 'ios' }}
run: |
echo $UNIT_IOS_GOOGLE_SERVICES | base64 -d > GoogleService-Info.plist

- name: 📋 Update package.json Versions
run: |
# Ensure jq exists on both Linux and macOS
Expand Down Expand Up @@ -287,33 +295,137 @@ jobs:

- name: 📋 Prepare Release Notes file
if: ${{ matrix.platform == 'android' }}
env:
RELEASE_NOTES_INPUT: ${{ github.event.inputs.release_notes }}
PR_BODY: ${{ github.event.pull_request.body }}
run: |
set -eo pipefail
# Determine source of release notes: workflow input, PR body, or recent commits
if [ -n "$RELEASE_NOTES_INPUT" ]; then
NOTES="$RELEASE_NOTES_INPUT"
elif [ -n "$PR_BODY" ]; then
NOTES="$(printf '%s\n' "$PR_BODY" \

# Function to extract release notes from PR body
extract_release_notes() {
local body="$1"
# Try to extract content under "## Release Notes" heading
local notes="$(printf '%s\n' "$body" \
| awk 'f && /^## /{exit} /^## Release Notes/{f=1; next} f')"
else

# If no specific section found, use the entire body (up to first 500 chars for safety)
if [ -z "$notes" ]; then
notes="$(printf '%s\n' "$body" | head -c 500)"
fi

printf '%s\n' "$notes"
}

# Determine source of release notes
NOTES=""

# Check if this was triggered by a push event (likely a merge)
if [ "${{ github.event_name }}" = "push" ]; then
echo "Fetching PR body for merged commit..."

# Get the PR number associated with this merge commit
PR_NUMBER=$(gh pr list \
--state merged \
--search "${{ github.sha }}" \
--json number \
--jq '.[0].number' 2>/dev/null || echo "")

if [ -n "$PR_NUMBER" ]; then
echo "Found merged PR #$PR_NUMBER"

# Fetch the PR body
PR_BODY=$(gh pr view "$PR_NUMBER" --json body --jq '.body' 2>/dev/null || echo "")

if [ -n "$PR_BODY" ]; then
NOTES="$(extract_release_notes "$PR_BODY")"
fi
else
echo "No associated PR found, checking commit message..."
# Try to find PR number from commit message
PR_FROM_COMMIT=$(git log -1 --pretty=%B | grep -oE '#[0-9]+' | head -1 | tr -d '#' || echo "")

if [ -n "$PR_FROM_COMMIT" ]; then
echo "Found PR #$PR_FROM_COMMIT from commit message"
PR_BODY=$(gh pr view "$PR_FROM_COMMIT" --json body --jq '.body' 2>/dev/null || echo "")

if [ -n "$PR_BODY" ]; then
NOTES="$(extract_release_notes "$PR_BODY")"
fi
fi
fi
fi

# Fallback to recent commits if no PR body found
if [ -z "$NOTES" ]; then
echo "No PR body found, using recent commits..."
NOTES="$(git log -n 5 --pretty=format:'- %s')"
fi

# Fail if no notes extracted
if [ -z "$NOTES" ]; then
echo "Error: No release notes extracted" >&2
exit 1
fi

# Write header and notes to file
{
echo "## Version 7.${{ github.run_number }} - $(date +%Y-%m-%d)"
echo
printf '%s\n' "$NOTES"
} > RELEASE_NOTES.md

echo "Release notes prepared:"
cat RELEASE_NOTES.md
env:
GH_TOKEN: ${{ github.token }}

Comment on lines 288 to +380
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix shell script quote mismatch and improve PR detection logic.

Several issues in the release notes extraction script:

  1. Quote mismatch at line 306: The awk pattern has mismatched quotes (' at start, " at end)
  2. PR detection may fail: Line 326 uses gh pr list --search "${{ github.sha }}" which searches PR content, not commits. This won't reliably find the PR that introduced the commit.
  3. No handling for multiple results: The script assumes exactly one PR is returned.

Apply these fixes:

           extract_release_notes() {
             local body="$1"
             # Try to extract content under "## Release Notes" heading
-            local notes="$(printf '%s\n' "$body" \
-              | awk 'f && /^## /{exit} /^## Release Notes/{f=1; next} f')"
+            local notes="$(printf '%s\n' "$body" \
+              | awk 'flag && /^## /{exit} /^## Release Notes/{flag=1; next} flag')"

For better PR detection, use the GitHub API to find PRs by commit SHA:

             # Get the PR number associated with this merge commit
-            PR_NUMBER=$(gh pr list \
-              --state merged \
-              --search "${{ github.sha }}" \
-              --json number \
-              --jq '.[0].number' 2>/dev/null || echo "")
+            # Use the GitHub API to find the PR that contains this commit
+            PR_NUMBER=$(gh api "repos/${{ github.repository }}/commits/${{ github.sha }}/pulls" \
+              --jq '.[0].number' 2>/dev/null || echo "")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: 📋 Prepare Release Notes file
if: ${{ matrix.platform == 'android' }}
env:
RELEASE_NOTES_INPUT: ${{ github.event.inputs.release_notes }}
PR_BODY: ${{ github.event.pull_request.body }}
run: |
set -eo pipefail
# Determine source of release notes: workflow input, PR body, or recent commits
if [ -n "$RELEASE_NOTES_INPUT" ]; then
NOTES="$RELEASE_NOTES_INPUT"
elif [ -n "$PR_BODY" ]; then
NOTES="$(printf '%s\n' "$PR_BODY" \
# Function to extract release notes from PR body
extract_release_notes() {
local body="$1"
# Try to extract content under "## Release Notes" heading
local notes="$(printf '%s\n' "$body" \
| awk 'f && /^## /{exit} /^## Release Notes/{f=1; next} f')"
else
# If no specific section found, use the entire body (up to first 500 chars for safety)
if [ -z "$notes" ]; then
notes="$(printf '%s\n' "$body" | head -c 500)"
fi
printf '%s\n' "$notes"
}
# Determine source of release notes
NOTES=""
# Check if this was triggered by a push event (likely a merge)
if [ "${{ github.event_name }}" = "push" ]; then
echo "Fetching PR body for merged commit..."
# Get the PR number associated with this merge commit
PR_NUMBER=$(gh pr list \
--state merged \
--search "${{ github.sha }}" \
--json number \
--jq '.[0].number' 2>/dev/null || echo "")
if [ -n "$PR_NUMBER" ]; then
echo "Found merged PR #$PR_NUMBER"
# Fetch the PR body
PR_BODY=$(gh pr view "$PR_NUMBER" --json body --jq '.body' 2>/dev/null || echo "")
if [ -n "$PR_BODY" ]; then
NOTES="$(extract_release_notes "$PR_BODY")"
fi
else
echo "No associated PR found, checking commit message..."
# Try to find PR number from commit message
PR_FROM_COMMIT=$(git log -1 --pretty=%B | grep -oE '#[0-9]+' | head -1 | tr -d '#' || echo "")
if [ -n "$PR_FROM_COMMIT" ]; then
echo "Found PR #$PR_FROM_COMMIT from commit message"
PR_BODY=$(gh pr view "$PR_FROM_COMMIT" --json body --jq '.body' 2>/dev/null || echo "")
if [ -n "$PR_BODY" ]; then
NOTES="$(extract_release_notes "$PR_BODY")"
fi
fi
fi
fi
# Fallback to recent commits if no PR body found
if [ -z "$NOTES" ]; then
echo "No PR body found, using recent commits..."
NOTES="$(git log -n 5 --pretty=format:'- %s')"
fi
# Fail if no notes extracted
if [ -z "$NOTES" ]; then
echo "Error: No release notes extracted" >&2
exit 1
fi
# Write header and notes to file
{
echo "## Version 7.${{ github.run_number }} - $(date +%Y-%m-%d)"
echo
printf '%s\n' "$NOTES"
} > RELEASE_NOTES.md
echo "Release notes prepared:"
cat RELEASE_NOTES.md
env:
GH_TOKEN: ${{ github.token }}
- name: 📋 Prepare Release Notes file
if: ${{ matrix.platform == 'android' }}
run: |
set -eo pipefail
# Function to extract release notes from PR body
extract_release_notes() {
local body="$1"
# Try to extract content under "## Release Notes" heading
local notes="$(printf '%s\n' "$body" \
| awk 'flag && /^## /{exit} /^## Release Notes/{flag=1; next} flag')"
# If no specific section found, use the entire body (up to first 500 chars for safety)
if [ -z "$notes" ]; then
notes="$(printf '%s\n' "$body" | head -c 500)"
fi
printf '%s\n' "$notes"
}
# Determine source of release notes
NOTES=""
# Check if this was triggered by a push event (likely a merge)
if [ "${{ github.event_name }}" = "push" ]; then
echo "Fetching PR body for merged commit..."
# Use the GitHub API to find the PR that contains this commit
PR_NUMBER=$(gh api "repos/${{ github.repository }}/commits/${{ github.sha }}/pulls" \
--jq '.[0].number' 2>/dev/null || echo "")
if [ -n "$PR_NUMBER" ]; then
echo "Found merged PR #$PR_NUMBER"
# Fetch the PR body
PR_BODY=$(gh pr view "$PR_NUMBER" --json body --jq '.body' 2>/dev/null || echo "")
if [ -n "$PR_BODY" ]; then
NOTES="$(extract_release_notes "$PR_BODY")"
fi
else
echo "No associated PR found, checking commit message..."
# Try to find PR number from commit message
PR_FROM_COMMIT=$(git log -1 --pretty=%B | grep -oE '#[0-9]+' | head -1 | tr -d '#' || echo "")
if [ -n "$PR_FROM_COMMIT" ]; then
echo "Found PR #$PR_FROM_COMMIT from commit message"
PR_BODY=$(gh pr view "$PR_FROM_COMMIT" --json body --jq '.body' 2>/dev/null || echo "")
if [ -n "$PR_BODY" ]; then
NOTES="$(extract_release_notes "$PR_BODY")"
fi
fi
fi
fi
# Fallback to recent commits if no PR body found
if [ -z "$NOTES" ]; then
echo "No PR body found, using recent commits..."
NOTES="$(git log -n 5 --pretty=format:'- %s')"
fi
# Fail if no notes extracted
if [ -z "$NOTES" ]; then
echo "Error: No release notes extracted" >&2
exit 1
fi
# Write header and notes to file
{
echo "## Version 7.${{ github.run_number }} - $(date +%Y-%m-%d)"
echo
printf '%s\n' "$NOTES"
} > RELEASE_NOTES.md
echo "Release notes prepared:"
cat RELEASE_NOTES.md
env:
GH_TOKEN: ${{ github.token }}

- name: � Send Release Notes to Changerawr
if: ${{ matrix.platform == 'android' && env.CHANGERAWR_API_URL != '' && env.CHANGERAWR_API_KEY != '' }}
run: |
set -eo pipefail

# Read release notes
RELEASE_NOTES=$(cat RELEASE_NOTES.md)
VERSION="7.${{ github.run_number }}"

# Prepare JSON payload
PAYLOAD=$(jq -n \
--arg version "$VERSION" \
--arg notes "$RELEASE_NOTES" \
--arg platform "android" \
--arg buildNumber "${{ github.run_number }}" \
--arg commitSha "${{ github.sha }}" \
--arg buildUrl "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
'{
version: $version,
releaseNotes: $notes,
platform: $platform,
buildNumber: $buildNumber,
commitSha: $commitSha,
buildUrl: $buildUrl,
timestamp: now | todate
}')

echo "Sending release notes to Changerawr..."

# Send to Changerawr API
RESPONSE=$(curl -X POST "$CHANGERAWR_API_URL" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $CHANGERAWR_API_KEY" \
-d "$PAYLOAD" \
-w "\n%{http_code}" \
-s)

HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
RESPONSE_BODY=$(echo "$RESPONSE" | sed '$d')

if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 300 ]; then
echo "✅ Successfully sent release notes to Changerawr (HTTP $HTTP_CODE)"
echo "Response: $RESPONSE_BODY"
else
echo "⚠️ Failed to send release notes to Changerawr (HTTP $HTTP_CODE)"
echo "Response: $RESPONSE_BODY"
# Don't fail the build, just warn
fi

Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
- name: 📦 Create Release
- name: 📦 Create Release
if: ${{ matrix.platform == 'android' && (github.event.inputs.buildType == 'all' || github.event_name == 'push' || github.event.inputs.buildType == 'prod-apk') }}
uses: ncipollo/release-action@v1
with:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ yarn-error.log
/build
/automatic-build
google-services.json
GoogleService-Info.plist
credentials.json
Gemfile.lock
Gemfile
Expand Down
2 changes: 0 additions & 2 deletions __mocks__/react-native-ble-plx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ export class BleManager {
private static mockDevices: Device[] = [];
private static stateListener: ((state: State) => void) | null = null;

constructor() {}

static setMockState(state: State) {
this.mockState = state;
if (this.stateListener) {
Expand Down
3 changes: 3 additions & 0 deletions app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
supportsTablet: true,
bundleIdentifier: Env.BUNDLE_ID,
requireFullScreen: true,
googleServicesFile: 'GoogleService-Info.plist',
infoPlist: {
UIBackgroundModes: ['remote-notification', 'audio', 'bluetooth-central', 'voip'],
ITSAppUsesNonExemptEncryption: false,
Expand Down Expand Up @@ -214,6 +215,7 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
},
ios: {
deploymentTarget: '18.1',
useFrameworks: 'static',
},
},
],
Expand Down Expand Up @@ -262,6 +264,7 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
'@livekit/react-native-expo-plugin',
'@config-plugins/react-native-webrtc',
'@config-plugins/react-native-callkeep',
'@react-native-firebase/app',
'./customGradle.plugin.js',
'./customManifest.plugin.js',
['app-icon-badge', appIconBadgeConfig],
Expand Down
16 changes: 16 additions & 0 deletions eas.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,22 @@
"EXPO_NO_DOTENV": "1"
}
},
"dev-sim": {
"developmentClient": false,
"yarn": "1.22.22",
"ios": {
"simulator": true,
"image": "latest"
},
"android": {
"buildType": "apk",
"image": "latest"
},
"env": {
"APP_ENV": "development",
"EXPO_NO_DOTENV": "1"
}
},
"simulator": {
"yarn": "1.22.22",
"ios": {
Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@
"@notifee/react-native": "^9.1.8",
"@novu/react-native": "~2.6.6",
"@react-native-community/netinfo": "^11.4.1",
"@react-native-firebase/analytics": "^23.5.0",
"@react-native-firebase/app": "^23.5.0",
"@react-native-firebase/messaging": "^23.5.0",
"@rnmapbox/maps": "10.1.42-rc.0",
"@semantic-release/git": "^10.0.1",
"@sentry/react-native": "~6.14.0",
Expand Down Expand Up @@ -122,7 +125,7 @@
"expo-localization": "~16.1.6",
"expo-location": "~18.1.6",
"expo-navigation-bar": "~4.2.8",
"expo-notifications": "~0.31.4",
"expo-notifications": "0.28.3",
"expo-router": "~5.1.7",
"expo-screen-orientation": "~8.1.7",
"expo-sharing": "~13.1.5",
Expand Down
22 changes: 9 additions & 13 deletions src/components/calls/dispatch-selection-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
const { data, selection, isLoading, error, searchQuery, fetchDispatchData, setSelection, toggleEveryone, toggleUser, toggleGroup, toggleRole, toggleUnit, setSearchQuery, clearSelection, getFilteredData } =
useDispatchStore();

const filteredData = useMemo(() => getFilteredData(), [data, searchQuery]);
const filteredData = useMemo(() => getFilteredData(), [getFilteredData]);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

useEffect(() => {
if (isVisible) {
Expand Down Expand Up @@ -121,9 +121,8 @@
<TouchableOpacity onPress={() => toggleUser(user.Id)}>
<HStack className="items-center space-x-3">
<Box
className={`size-5 items-center justify-center rounded border-2 ${
selection.users.includes(user.Id) ? 'border-blue-500 bg-blue-500' : colorScheme === 'dark' ? 'border-neutral-600' : 'border-neutral-300'
}`}
className={`size-5 items-center justify-center rounded border-2 ${selection.users.includes(user.Id) ? 'border-blue-500 bg-blue-500' : colorScheme === 'dark' ? 'border-neutral-600' : 'border-neutral-300'

Check warning on line 124 in src/components/calls/dispatch-selection-modal.tsx

View workflow job for this annotation

GitHub Actions / test

Insert `⏎··························`
}`}

Check warning on line 125 in src/components/calls/dispatch-selection-modal.tsx

View workflow job for this annotation

GitHub Actions / test

Delete `··`
>
{selection.users.includes(user.Id) && <CheckIcon size={12} className="text-white" />}
</Box>
Expand All @@ -148,9 +147,8 @@
<TouchableOpacity onPress={() => toggleGroup(group.Id)}>
<HStack className="items-center space-x-3">
<Box
className={`size-5 items-center justify-center rounded border-2 ${
selection.groups.includes(group.Id) ? 'border-blue-500 bg-blue-500' : colorScheme === 'dark' ? 'border-neutral-600' : 'border-neutral-300'
}`}
className={`size-5 items-center justify-center rounded border-2 ${selection.groups.includes(group.Id) ? 'border-blue-500 bg-blue-500' : colorScheme === 'dark' ? 'border-neutral-600' : 'border-neutral-300'

Check warning on line 150 in src/components/calls/dispatch-selection-modal.tsx

View workflow job for this annotation

GitHub Actions / test

Insert `⏎··························`
}`}

Check warning on line 151 in src/components/calls/dispatch-selection-modal.tsx

View workflow job for this annotation

GitHub Actions / test

Delete `··`
>
{selection.groups.includes(group.Id) && <CheckIcon size={12} className="text-white" />}
</Box>
Expand All @@ -175,9 +173,8 @@
<TouchableOpacity onPress={() => toggleRole(role.Id)}>
<HStack className="items-center space-x-3">
<Box
className={`size-5 items-center justify-center rounded border-2 ${
selection.roles.includes(role.Id) ? 'border-blue-500 bg-blue-500' : colorScheme === 'dark' ? 'border-neutral-600' : 'border-neutral-300'
}`}
className={`size-5 items-center justify-center rounded border-2 ${selection.roles.includes(role.Id) ? 'border-blue-500 bg-blue-500' : colorScheme === 'dark' ? 'border-neutral-600' : 'border-neutral-300'

Check warning on line 176 in src/components/calls/dispatch-selection-modal.tsx

View workflow job for this annotation

GitHub Actions / test

Insert `⏎··························`
}`}

Check warning on line 177 in src/components/calls/dispatch-selection-modal.tsx

View workflow job for this annotation

GitHub Actions / test

Delete `··`
>
{selection.roles.includes(role.Id) && <CheckIcon size={12} className="text-white" />}
</Box>
Expand All @@ -202,9 +199,8 @@
<TouchableOpacity onPress={() => toggleUnit(unit.Id)}>
<HStack className="items-center space-x-3">
<Box
className={`size-5 items-center justify-center rounded border-2 ${
selection.units.includes(unit.Id) ? 'border-blue-500 bg-blue-500' : colorScheme === 'dark' ? 'border-neutral-600' : 'border-neutral-300'
}`}
className={`size-5 items-center justify-center rounded border-2 ${selection.units.includes(unit.Id) ? 'border-blue-500 bg-blue-500' : colorScheme === 'dark' ? 'border-neutral-600' : 'border-neutral-300'

Check warning on line 202 in src/components/calls/dispatch-selection-modal.tsx

View workflow job for this annotation

GitHub Actions / test

Insert `⏎··························`
}`}

Check warning on line 203 in src/components/calls/dispatch-selection-modal.tsx

View workflow job for this annotation

GitHub Actions / test

Delete `··`
>
{selection.units.includes(unit.Id) && <CheckIcon size={12} className="text-white" />}
</Box>
Expand Down
Loading