Skip to content
61 changes: 58 additions & 3 deletions .github/workflows/testflight.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
name: TestFlight

on:
workflow_dispatch:
push:
branches: [main]
pull_request:
branches: [main]
types: [opened, edited]
Expand All @@ -22,6 +22,7 @@
name: Validate trigger
if: >
github.event_name == 'push' ||
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name == github.repository) ||
github.event_name == 'issue_comment'
Expand Down Expand Up @@ -52,7 +53,7 @@
if (netbirdMatch) core.setOutput('netbird-ref', netbirdMatch[1]);
}

if (context.eventName === 'push') {
if (context.eventName === 'workflow_dispatch' || context.eventName === 'push') {
core.setOutput('ref', context.sha);
core.setOutput('should-build', 'true');
core.setOutput('upload', 'true');
Expand Down Expand Up @@ -149,6 +150,48 @@
echo "version=$NEXT" >> "$GITHUB_OUTPUT"
echo "Tag: $LATEST_TAG → next: $NEXT"

- name: Fetch latest build number from App Store Connect
if: steps.pre.outputs.should-build == 'true'
id: asc-build
env:
ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
KEY_ID: ${{ secrets.APP_STORE_CONNECT_KEY_ID }}
PRIVATE_KEY_BASE64: ${{ secrets.APP_STORE_CONNECT_PRIVATE_KEY }}
APP_ID: ${{ secrets.APP_STORE_APP_ID_IOS }}
run: |
VERSION="${{ steps.pre.outputs.version-override || steps.derive.outputs.version }}"

echo "$PRIVATE_KEY_BASE64" | base64 --decode > /tmp/AuthKey.p8

JWT=$(python3 -c "import base64,json,time,os;from cryptography.hazmat.primitives import hashes,serialization;from cryptography.hazmat.primitives.asymmetric import ec;from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature;key=serialization.load_pem_private_key(open('/tmp/AuthKey.p8','rb').read(),password=None);b64url=lambda d:base64.urlsafe_b64encode(d if isinstance(d,bytes) else d.encode()).rstrip(b'=').decode();now=int(time.time());h=b64url(json.dumps({'alg':'ES256','kid':os.environ['KEY_ID'],'typ':'JWT'},separators=(',',':')));p=b64url(json.dumps({'iss':os.environ['ISSUER_ID'],'exp':now+1200,'aud':'appstoreconnect-v1'},separators=(',',':')));msg=f'{h}.{p}'.encode();sig_der=key.sign(msg,ec.ECDSA(hashes.SHA256()));r,s=decode_dss_signature(sig_der);sig=b64url(r.to_bytes(32,'big')+s.to_bytes(32,'big'));print(f'{h}.{p}.{sig}')")
Comment thread
evgeniyChepelev marked this conversation as resolved.

HTTP_STATUS=$(curl -sg \
-o /tmp/asc_response.json \
-w "%{http_code}" \
"https://api.appstoreconnect.apple.com/v1/builds?filter[app]=$APP_ID&filter[preReleaseVersion.version]=$VERSION&sort=-uploadedDate&limit=1" \
-H "Authorization: Bearer $JWT")

RESPONSE=$(cat /tmp/asc_response.json)
echo "HTTP status: $HTTP_STATUS"

if [ "$HTTP_STATUS" != "200" ]; then
echo "API error response:"
echo "$RESPONSE" | jq . || echo "$RESPONSE"
exit 1
fi

LATEST_BUILD=$(echo "$RESPONSE" | jq -r 'if (.data | length) > 0 then .data[0].attributes.version else "none" end')

echo "latest-build=$LATEST_BUILD" >> "$GITHUB_OUTPUT"

echo "========================================="
echo " App Store Connect — latest build info"
echo " Version: $VERSION"
echo " Latest uploaded build: $LATEST_BUILD"
echo "========================================="

rm -f /tmp/AuthKey.p8
Comment thread
evgeniyChepelev marked this conversation as resolved.
Outdated

- name: Finalize outputs
id: finalize
uses: actions/github-script@v7
Expand All @@ -157,9 +200,21 @@
core.setOutput('ref', '${{ steps.pre.outputs.ref }}');
core.setOutput('should-build', '${{ steps.pre.outputs.should-build }}');
core.setOutput('upload', '${{ steps.pre.outputs.upload }}');
core.setOutput('build-number', '${{ steps.pre.outputs.build-number }}');
core.setOutput('netbird-ref', '${{ steps.pre.outputs.netbird-ref }}');

const overrideBuild = '${{ steps.pre.outputs.build-number }}';
const latestBuild = '${{ steps.asc-build.outputs.latest-build }}';

Check failure

Code scanning / CodeQL

Code injection Critical test

Potential code injection in
${ steps.asc-build.outputs.latest-build }
, which may be controlled by an external user (
issue_comment
).
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
let buildNumber;
if (overrideBuild && overrideBuild !== '${{ github.run_number }}') {
buildNumber = overrideBuild;
} else if (latestBuild && latestBuild !== 'none') {
buildNumber = String(parseInt(latestBuild, 10) + 1);
} else {
buildNumber = '1';
}
core.info(`build-number: ${buildNumber} (latest=${latestBuild}, override=${overrideBuild})`);
core.setOutput('build-number', buildNumber);

const override = '${{ steps.pre.outputs.version-override }}';
const derived = '${{ steps.derive.outputs.version }}';
core.setOutput('version', override || derived);
Expand Down
Loading