Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
c494713
Fix Core Data migration startup crashes
takemiyamakoto Jul 26, 2026
d7648e2
Fix production iOS release identity
takemiyamakoto Jul 26, 2026
b851725
Guard the production iOS archive identity
takemiyamakoto Jul 26, 2026
1d04582
Bind the iOS build number to release settings
takemiyamakoto Jul 26, 2026
749dbdd
Match WalletConnect storage to signed app groups
takemiyamakoto Jul 26, 2026
5ebcf5e
Keep TODO debt baseline current
takemiyamakoto Jul 26, 2026
b11cdc5
Update the Core Data release test invariant
takemiyamakoto Jul 26, 2026
1424b59
Clarify the Core Data release cohort
takemiyamakoto Jul 26, 2026
bcc3997
Keep custom-only chains available after migration
takemiyamakoto Jul 26, 2026
7a819cb
Isolate Debug builds from production wallet data
takemiyamakoto Jul 26, 2026
d689535
Gate and sign the migration crash hotfix release
takemiyamakoto Jul 26, 2026
e6cb0e9
Scope TestFlight provisioning to the app target
takemiyamakoto Jul 26, 2026
afbcde6
Scope App Store signing to the release target
takemiyamakoto Jul 26, 2026
f56b7b8
Accept standard Xcode archive application paths
takemiyamakoto Jul 26, 2026
5469d34
docs: track TestFlight publication evidence
takemiyamakoto Jul 26, 2026
02e7620
build: bump TestFlight build for in-place update
takemiyamakoto Jul 26, 2026
cdb8474
fix: make wallet observation activation lossless
takemiyamakoto Jul 26, 2026
ceb0ed8
build: advance TestFlight hotfix to 2026.7.28
takemiyamakoto Jul 26, 2026
3f00f46
ci: pin workflow actions for release safety
takemiyamakoto Jul 26, 2026
9c89b26
fix: prohibit empty meta account codecs
takemiyamakoto Jul 26, 2026
838840b
test: isolate account export operations
takemiyamakoto Jul 26, 2026
f7e44e6
ci: use pinned Codecov uploader with OIDC
takemiyamakoto Jul 26, 2026
10164c0
ci: generate executable iOS coverage data
takemiyamakoto Jul 26, 2026
ef07cca
chore: scope migration lint suppressions
takemiyamakoto Jul 30, 2026
ab9a73d
test: bind copied phone fixture into release gate
takemiyamakoto Jul 30, 2026
5a78687
Gate iOS transaction builder tests in CI
takemiyamakoto Jul 1, 2026
57002df
Consolidate iOS production readiness changes
takemiyamakoto Jul 31, 2026
0fd6fd2
test(ios): remove chain registry observer race
takemiyamakoto Jul 31, 2026
2e45e55
fix(ios): preserve TON journals across OS upgrades
takemiyamakoto Jul 31, 2026
ddb3b9e
fix(ci): update Codecov verification endpoint
takemiyamakoto Jul 31, 2026
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
2 changes: 1 addition & 1 deletion .github/workflows/branch-flow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
timeout-minutes: 5
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Branch flow audit self-test
run: bash scripts/test-branch-flow-audit.sh
Expand Down
81 changes: 78 additions & 3 deletions .github/workflows/codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,38 @@ on:
- develop
- master

permissions:
contents: read

jobs:
build:
runs-on: macOS-latest
permissions:
contents: read
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false

- name: Codecov uploader supply-chain audit
run: |
bash ./scripts/test-codecov-workflow-audit.sh
bash ./scripts/audit-codecov-workflow.sh

- name: Branch flow audit
run: |
bash ./scripts/audit-branch-flow.sh
bash ./scripts/test-branch-flow-audit.sh

- name: Release signing gate audit
run: |
bash ./scripts/test-release-signing-gate-audit.sh
bash ./scripts/test-release-signing-gate.sh
bash ./scripts/test-release-signing-bootstrap.sh
bash ./scripts/audit-release-signing-gate.sh

- name: Audit public artifacts
run: ./scripts/audit-public-artifacts.sh

Expand All @@ -30,12 +50,34 @@ jobs:
bash ./scripts/test-todo-debt-audit.sh
bash ./scripts/audit-todo-debt.sh

- name: TestFlight publication evidence
run: |
bash ./scripts/test-testflight-publication-readiness-audit.sh
bash ./scripts/audit-testflight-publication-readiness.sh

- name: Codecov executable coverage contract
run: |
bash ./scripts/test-codecov-coverage-contract.sh
bash ./scripts/audit-codecov-coverage-contract.sh

- name: Transaction builder coverage audit
run: |
bash ./scripts/test-transaction-builder-tests-audit.sh
bash ./scripts/audit-transaction-builder-tests.sh

- name: TON production send readiness
run: |
bash ./scripts/test-ton-production-send-readiness-audit.sh
bash ./scripts/audit-ton-production-send-readiness.sh

- name: Iroha mobile SDK release asset contract
env:
GH_TOKEN: ${{ github.token }}
IROHA_MOBILE_SDK_RELEASE_TAG: ${{ vars.IROHA_MOBILE_SDK_RELEASE_TAG }}
IROHA_MOBILE_SDK_RELEASE_REPO: ${{ vars.IROHA_MOBILE_SDK_RELEASE_REPO }}
run: |
bash ./scripts/test-iroha-production-send-readiness-audit.sh
bash ./scripts/audit-iroha-production-send-readiness.sh
bash ./scripts/check-iroha-mobile-sdk-release-assets.sh --self-test
if [[ -n "${IROHA_MOBILE_SDK_RELEASE_TAG:-}" ]]; then
bash ./scripts/check-iroha-mobile-sdk-release-assets.sh --download --tag "$IROHA_MOBILE_SDK_RELEASE_TAG"
Expand All @@ -53,7 +95,7 @@ jobs:

- name: Upload shared-features delta report
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: shared-features-delta-report
path: build/reports/shared-features-delta-report.json
Expand Down Expand Up @@ -145,13 +187,16 @@ jobs:
esac
echo "Using destination: $DESTINATION"
LOG_FILE="$PWD/build/codecov-xcodebuild.log"
RESULT_BUNDLE="$PWD/build/CodecovTests.xcresult"
mkdir -p "$PWD/build"
if ! xcodebuild \
-workspace fearless.xcworkspace \
-scheme fearless.tests \
-clonedSourcePackagesDirPath "$SP_DIR" \
-disableAutomaticPackageResolution \
-destination "$DESTINATION" \
-enableCodeCoverage YES \
-resultBundlePath "$RESULT_BUNDLE" \
build test | tee "$LOG_FILE" | xcpretty --test; then
echo "Build/Test failed. Key errors from $LOG_FILE:" >&2
if command -v rg >/dev/null 2>&1; then
Expand All @@ -161,9 +206,39 @@ jobs:
fi
exit 1
fi
COVERAGE_PROFILE="$(
find "$HOME/Library/Developer/Xcode/DerivedData" \
-type f -name '*.profdata' -print -quit
)"
if [[ -z "$COVERAGE_PROFILE" ]]; then
echo "Tests passed but Xcode produced no coverage profile." >&2
exit 1
fi
xcrun llvm-profdata show "$COVERAGE_PROFILE" >/dev/null || {
echo "Xcode produced an unreadable coverage profile: $COVERAGE_PROFILE" >&2
exit 1
}
test -d "$RESULT_BUNDLE" || {
echo "Xcode did not produce the required result bundle." >&2
exit 1
}

- name: Signed Release archive gate
if: github.event_name == 'push'
env:
IOS_RELEASE_SIGNING_REQUIRED: '1'
IOS_RELEASE_CERTIFICATE_P12_BASE64: ${{ secrets.IOS_RELEASE_CERTIFICATE_P12_BASE64 }}
IOS_RELEASE_CERTIFICATE_PASSWORD: ${{ secrets.IOS_RELEASE_CERTIFICATE_PASSWORD }}
IOS_RELEASE_PROVISIONING_PROFILE_BASE64: ${{ secrets.IOS_RELEASE_PROVISIONING_PROFILE_BASE64 }}
run: bash ./scripts/ci/run-release-signing-ci.sh

- name: Upload coverage to Codecov
run: bash <(curl -s https://codecov.io/bash) -J 'fearless'
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
with:
fail_ci_if_error: true
plugins: xcode
swift_project: fearless
use_oidc: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}

jenkins-pr-merge:
name: continuous-integration/jenkins/pr-merge
Expand Down
68 changes: 68 additions & 0 deletions .github/workflows/ios-release-safety.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: iOS Release Safety

on:
pull_request:
push:
branches:
- master
- "release/**"
- "hotfix/**"
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ios-release-safety-${{ github.ref }}
cancel-in-progress: true

jobs:
release-contracts:
name: release-contracts
runs-on: macos-15
timeout-minutes: 20
steps:
- name: Check out exact commit
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 1

- name: Require clean exact checkout
run: |
set -euo pipefail
test -z "$(git status --porcelain=v1 --untracked-files=all)"
git rev-parse --verify HEAD

- name: Run release gate contract suites
run: |
set -euo pipefail
bash scripts/test-ios-release-identity-audit.sh
bash scripts/test-ios-release-archive-signing-contract.sh
bash scripts/test-ios-signed-release-artifact-audit.sh
bash scripts/test-coredata-release-gate.sh
bash scripts/test-coredata-simulator-rehearsal.sh
bash scripts/storage/test-user-storage-compatibility-model-audit.sh

- name: Reject silent x86 Release omissions
run: |
set -euo pipefail
if rg -n 'Skipping Release simulator tests|Release simulator tests were skipped' scripts/test-matrix.sh; then
echo "Release matrix still contains a green skip path." >&2
exit 1
fi

required-context:
name: ios-release-safety
runs-on: ubuntu-latest
needs: release-contracts
if: always()
steps:
- name: Require release contract success
env:
RESULT: ${{ needs.release-contracts.result }}
run: |
set -euo pipefail
[[ "$RESULT" == "success" ]] || {
echo "iOS release safety contracts did not pass: $RESULT" >&2
exit 1
}
111 changes: 104 additions & 7 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ def ghStatusFallback(String context, String state, String description) {

// Ensure SPM and shared-features patches are applied before the main pipeline.
// This resolves Web3 API drift (Data.bytes) and IrohaCrypto modulemap issues prior to archive.
node('mac-fearless') {
try {
node('mac-fearless') {
stage('Bootstrap CI deps') {
checkout scm
sh label: 'Bootstrap Pods/SPM/LFS + apply shared-features fixes', script: 'bash scripts/ci/bootstrap.sh'
Expand Down Expand Up @@ -78,17 +79,113 @@ node('mac-fearless') {
scripts/test-matrix.sh fearless.tests "$DESTINATION"
'''
ghNotifySafe context: 'jenkins/ios-tests', status: 'SUCCESS', description: 'All tests passed'
ghNotifySafe context: 'continuous-integration/jenkins/pr-merge', status: 'SUCCESS', description: 'Jenkins PR merge build passed'
ghStatusFallback('jenkins/ios-tests', 'success', 'All tests passed')
ghStatusFallback('continuous-integration/jenkins/pr-merge', 'success', 'Jenkins PR merge build passed')
} catch (e) {
ghNotifySafe context: 'jenkins/ios-tests', status: 'FAILURE', description: 'Unit tests failed'
ghNotifySafe context: 'continuous-integration/jenkins/pr-merge', status: 'FAILURE', description: 'Jenkins PR merge build failed'
ghStatusFallback('jenkins/ios-tests', 'failure', 'Unit tests failed')
ghStatusFallback('continuous-integration/jenkins/pr-merge', 'failure', 'Jenkins PR merge build failed')
throw e
}
}
}

appPipeline.runPipeline('fearless')
ghNotifySafe context: 'jenkins/ios-release-safety', status: 'PENDING', description: 'Running release safety gates'
ghStatusFallback('jenkins/ios-release-safety', 'pending', 'Running release safety gates')
stage('iOS Release Safety Contracts') {
sh label: 'Run fail-closed release gate contract tests', script: '''
set -euo pipefail
bash scripts/test-ios-release-identity-audit.sh
bash scripts/test-ios-signed-release-artifact-audit.sh
bash scripts/test-coredata-release-gate.sh
bash scripts/test-coredata-simulator-rehearsal.sh
bash scripts/storage/test-user-storage-compatibility-model-audit.sh
'''
}

// Jenkins multibranch PR jobs expose BRANCH_NAME as PR-<number>. Bind the
// gate to both direct release branches and the repository's allowed hotfix /
// release promotion paths so a PR cannot bypass the exact Release evidence.
def directReleaseBranch = (
env.BRANCH_NAME == 'master' ||
env.BRANCH_NAME?.startsWith('release/') ||
env.BRANCH_NAME?.startsWith('hotfix/')
)
def releasePullRequest = (
env.CHANGE_ID &&
(
(
env.CHANGE_TARGET == 'develop' &&
env.CHANGE_BRANCH?.startsWith('hotfix/')
) ||
(
env.CHANGE_TARGET == 'master' &&
(
env.CHANGE_BRANCH == 'develop' ||
env.CHANGE_BRANCH?.startsWith('release/') ||
env.CHANGE_BRANCH?.startsWith('hotfix/')
)
)
)
)
def releaseBranch = directReleaseBranch || releasePullRequest
if (releaseBranch) {
stage('Exact Core Data Release Gate') {
sh label: 'Require arm64 and exact 412-test Release inventory', script: '''
set -euo pipefail
[[ "$(uname -m)" == "arm64" ]] || {
echo "Exact Release gate requires dispatch to an arm64 macOS agent." >&2
exit 78
}
IOS_EXPECTED_BUILD_NUMBER=2026.7.28 \
IOS_RELEASE_SOURCE_PACKAGES_DIR="$PWD/SourcePackages" \
bash scripts/ci/audit-ios-release-identity.sh
bash scripts/storage/audit-user-storage-compatibility-models.sh
bash scripts/ci/run-coredata-release-gate.sh --stage core
'''
}
}

if (env.IOS_RELEASE_CANDIDATE == '1') {
stage('Copied-phone Release Evidence') {
sh label: 'Require immutable copied-phone fixture stage', script: '''
set -euo pipefail
: "${FEARLESS_SUBSTRATE_PHONE_STORE_FIXTURE:?release candidate requires the copied-phone fixture}"
: "${FEARLESS_CORE_DATA_SIMULATOR_UDID:?release candidate requires an explicit Simulator UDID}"
bash scripts/ci/run-coredata-release-gate.sh \
--stage copied-phone \
--simulator-udid "$FEARLESS_CORE_DATA_SIMULATOR_UDID" \
--fixture "$FEARLESS_SUBSTRATE_PHONE_STORE_FIXTURE"
'''
}
}
}

appPipeline.runPipeline('fearless')

// A required context must not turn green until every custom gate and the
// shared application pipeline have completed. The fallback needs a node
// because it uses curl through the shell.
ghNotifySafe context: 'jenkins/ios-release-safety', status: 'SUCCESS', description: 'All iOS release safety gates passed'
ghNotifySafe context: 'continuous-integration/jenkins/pr-merge', status: 'SUCCESS', description: 'Jenkins PR merge build passed'
node('mac-fearless') {
stage('Finalize Required GitHub Statuses') {
ghStatusFallback('jenkins/ios-release-safety', 'success', 'All iOS release safety gates passed')
ghStatusFallback('continuous-integration/jenkins/pr-merge', 'success', 'Jenkins PR merge build passed')
}
}
} catch (e) {
// Post plugin failures first. If a macOS executor cannot be reacquired for
// the API fallback, the required contexts remain failed or pending, never
// stale green.
ghNotifySafe context: 'jenkins/ios-release-safety', status: 'FAILURE', description: 'iOS release safety or pipeline failed'
ghNotifySafe context: 'continuous-integration/jenkins/pr-merge', status: 'FAILURE', description: 'Jenkins PR merge build failed'
try {
node('mac-fearless') {
stage('Report Required GitHub Status Failure') {
ghStatusFallback('jenkins/ios-release-safety', 'failure', 'iOS release safety or pipeline failed')
ghStatusFallback('continuous-integration/jenkins/pr-merge', 'failure', 'Jenkins PR merge build failed')
}
}
} catch (statusError) {
echo "Could not run GitHub status API fallback: ${statusError.message}"
}
throw e
}
Loading
Loading