Repository navigation
ci(auth): AmplifyCognitoClient CI and integration-test infrastructure follow-ups - #4402
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4402 +/- ##
===========================================
+ Coverage 75.69% 87.86% +12.16%
===========================================
Files 1360 486 -874
Lines 57392 25183 -32209
===========================================
- Hits 43444 22126 -21318
+ Misses 13948 3057 -10891
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
The test selector ignored every .md file, but twelve integration host-app READMEs are Copy Bundle Resources of their test targets, so a change that deleted one selected nothing and broke that host app's build. The selector now reads the checkout's Xcode projects and treats a file a project copies into a bundle like any other host-app file; other documentation is still skipped. A project it can't read makes the docs under it run their group.
The workflow and script headers said the gate rejects matches on every error type it lists, but check enforces two rules, on AuthErrorConvertible and KeychainStoreError. The headers now name those rules and say the AWSCognitoAuthError and AuthError sites are only listed.
…leftover tmp file test
… directory however --dir spells them
…o its re-send is checked
… and skip only under the CI-only rule
…and the drift, key and reminder rules
…t the overlap cases prepare-run.sh tolerates
…commands, as CI does
…record now needs every CI Lambda
eff3a58 to
e62d191
Compare
…rlay's key expiry annotation
…iry and code_sink_key.py
…ce 120 or fewer are left
|
Update: the scheduled reminder workflow ( The code sink's API key is now created and extended for AppSync's 365-day maximum, and a rerun extends it once 120 or fewer days are left (e9074d1 and the three commits before it). The integration README keeps what's needed to run the tests. |
Summary
Follow-ups for the AmplifyCognitoClient beta's CI and integration-test infrastructure: the change selectors, the
client's own CI resources (a new MFA-required pool, a 365-day code sink API key, a Lambda drift check), fixes to the sandbox scripts, and new and corrected integration tests. No library source and no public API
changes.
CI selector
scripts/python/affected_targets.pyno longer skips a host app's README when an Xcode project in the checkout copiesit into a test bundle (twelve host-app READMEs are Copy Bundle Resources). Deleting or renaming one broke the build
of the target that copies it while the PR selected nothing. Such a README is now attributed like any other host-app
file, in every group file. With the integration groups it selects its host app's group. That catches the break only
where the group's PR jobs build the copying target: six of the twelve are copied only by stress-test targets
(
GraphQLAPIStressTests,AnalyticsStressTests,AuthStressTests,DatastoreStressTests,GeoStressTests,StorageStressTests), which no PR job builds, so for those the break still shows only instress_test.ymlafterthe merge. With the unit groups it selects AWSPluginsCore, whose
AmplifyPlugins/path covers every plugin hostapp, as for any other host-app file:
unit_test.ymlruns AWSPluginsCore's unit tests, and the threebuild_*workflows, which only check that something is selected, build the whole package on all their platforms. None of
those builds a host app. That is accepted. Documentation no project bundles is still skipped, and a project the
selector can't read makes the docs under it run their group. New cases are in
test_affected_targets.py, the unitgroups' included.
--repo-rootnow defaults to the checkout that holds the script rather than the current directory,and a root with no Xcode project at all is treated as unread (its docs count as bundled), so a run from another
directory no longer skips a bundled README. CI ran from the root already.
ci_script_self_tests.ymlalso runs on a change to anyproject.pbxproj, since the selector's self-test reads thereal Xcode projects.
integ_test.yml,integ_test_api.yml,integ_test_datastore.yml,unit_test.yml,build_amplify_swift_platforms.yml,build_minimum_supported_swift_platforms.yml,build_xcode_preview.yml) now also pass the renames' old paths, through a new--renamed-fromfile(
git diff --name-status -M --diff-filter=R, first path); the changed list keeps rename detection. The selectorattributes an old path like any changed file, so a renamed bundled README still selects its group and a file moved
out of a target still selects that target's group, but it skips an old path that nothing owns, covers or ignores
instead of failing closed, since the new path is already in the list. So a move that changes a target's directory
selects what the new paths select: moving
RecordValidationTests.swiftintoAmplifyRecordCache(c6bc9e3)selects 7 unit groups, not all 22. A plain deletion still fails closed.
auth_engine_gates.ymlandscripts/cognito-engine/error_match_gate.sh: the headers now name only the two errormatches the gate enforces, and state the risk for
AuthErrorConvertiblecorrectly: a cast on it matches AuthError,KeychainStoreError and the eight engine error types the plugin conforms, and silently misses the errors that are
only
EngineAuthErrorConvertible, so onlyAuthError(converting:)may make one.api-dump
This PR changes no public API. The "Public Interface Breakage Detection" workflow
(
api-breaking-changes-detection.yml), which would regenerate theapi-dump/*.jsonfiles and push an "Update APIdumps for new version" commit, is disabled in this repository, so no such commit comes to this branch.
Sandbox guard and scripts (
AmplifyClients/AmplifyCognitoClient/Tests/IntegrationTests/infra)AWS_ROLE_ARN,AWS_WEB_IDENTITY_TOKEN_FILE) or container credentials (AWS_CONTAINER_CREDENTIALS_*) are exported, not only whenAWS_PROFILEis unset. The first two would take over the profile; container credentials are used only when theprofile holds none, and are refused to be safe. It doesn't cover a profile with no credentials of its own: the CLI
then falls back to an instance role or a legacy credentials file, and the first run records that account. Closing
that is a follow-up. A provision is also refused in a region other than the one the state directory records, and,
on every run, when the sandbox's global role already trusts an identity pool in another region (another state
directory's sandbox; the IAM roles are global). The region refusal no longer suggests another state directory,
because that would build the second sandbox it warns about.
plugin-configs.py --dirrecognizes the owner's and the configured testconfiguration directory however the path isspelled (case, symlinks;
os.path.samefileon the target and its ancestors). A test pins thesamefilewalk withthe
/System/Volumes/Dataalias of the temporary directories, which only the walk recognizes (skipped where thereis no such alias).
state.jsonas soon as they exist, before their roles, so a teardownafter a failure there removes them.
prepare-run.shruns now finish: a bounded retry, "already exists", "already forgotten" and an expiredcode sink key the other run deleted first (
rotate-key) are tolerated, and the lock waits. The report showserinas not found only while another run resets her; any other
admin-get-usererror stops the run, as on main.self-sign-up.shturns self sign-up off again (bash 3.2 skips the EXIT trap on QUIT).npm cino longer leaves a half-built custom sender cached.ensure_domainreuses an adopted pool's own hosted-UI domain when no prefix is recorded.ccit-confirm-sign-ups too; preflight goes by the live selfsign-up lease (an unused constant removed); the sandbox has seven parity pools, and teardown deletes identity pools
by recorded ID only; the resource table has rows for P-13 and P-14.
leftover tmp file test, and the first signal forward is lost in the self sign-up test, so its re-send is checked.
CI resources (
infra/ci)ccit-ci-mfa-req-totp-sms, made frominfra/pools/mfa-req-totp-sms.jsonon Essentials. Itsciapp client allows SRP andUSER_AUTH, with noUSER_PASSWORD_AUTH. MFA is required, with TOTP and SMS. Itstrigger is the CI pre-sign-up trigger, and both custom senders are the discard sender, so Cognito sends nothing.
ci-overlay.shhas a newmfa-req-totp-smsrole. It puts that pool's outputs in place of the plugin'sMFA-required file for the client suite only, and checks first that the file requires MFA and that its
mfa_methodsis an array of exactly TOTP and SMS (not a string, not a superset). Until the file is uploaded, therole stays the plugin's.
integ_test_auth_client.ymlpassesemail-alias extended mfa-req-totp-sms, andrun_integration_tests.yml's usage error names the new role. The overlay's self-test pins both halves of the MFAcheck and the fallback when the file is missing (the email-alias and extended files are still overlaid, and the
plugin's own MFA-required file stays).
(still held to the external ID and
aws:SourceAccount), as it opens the key policy, and narrows it again after.The plan now says both, and the provisioner's header lists the trust among what a rerun updates.
its tag check, and made again by
infra/ci/provision-ci.sh --apply --scope all(with--uploadfor a pool). Theprovisioner's header and every message that advises a redeploy now say
--scope all:a plain
--applyis phase 1, which makes only two of the eight functions again and adds invoke permissions forthe device-alias pool only, while
lambda-driftand the baseline check still pass. The pool steps include theuploaded files that name the pool (
--uploadnever overwrites), and, forccit-ci-default, the rotation file, theidentity pool and the reset Lambda.
provision-ci.shcreates the key, and extends it in place, for 365 days, AppSync's maximum(
CreateApiKeyandUpdateApiKeytake 1 to 365 days from the call). The expiry is rounded down to the hour, asAppSync stores it, after a 10-minute allowance for a local clock ahead of AppSync's; the fake AWS CLI now refuses an
expiry outside AppSync's bounds, as AppSync does. A rerun reuses the key only while it lives more than 120 days, and
otherwise extends it, keeping its id, so the two uploaded files that name it
(
ccit-ci-default-amplify_outputs.json,ccit-ci-email-alias-codes-amplify_outputs.json) stay valid. Each runprints the key's expiry after it. An expired key is replaced by a new one, but
--uploadnever rewrites those twofiles, so the run names them, says how to replace them (check their tag, delete them by hand, rerun with
--upload) and refuses at the end.lambda_code.py).provision-ci.sh lambda-drift(read-only) checks each
ccit-ci-Lambda's name and purpose tag, callsGetFunction, downloads the deployed zipfrom its presigned
Code.Locationinto a private temporary directory, and compares its files, by path andcontent hash, with the checkout's zip. So a function made from the older
zip -Xzips of the same files, orzipped by another zlib, is in sync, and
CodeSha256is only a note. npm'snode_modules/.bin/links count onneither side, so the custom sender that main's
zip -q -X -rbuilt (which holdsnode_modules/.bin/uuidas afile) is in sync. A DRIFT names the function and the files that changed, are only deployed, or are only in the
checkout. A function the baseline records that is not deployed is DRIFT too.
lambda-drift --recordprints thatcomparison and records nothing; only
--record --accept-deployedwrites the deployed files (paths and contenthashes) with the sources' digest to
infra/ci/lambda-baseline.json(not committed yet), which then counts as theaccepted deployment until the sources change. A record needs every Lambda:
--recordwrites nothing unless alleight CI Lambdas are deployed (the first record too), and
--accept-deployedrefuses while a function was notcompared (the custom sender without npm).
lambda-driftrefuses another region than the CI resources' (as--applydoes), and with noccit-ci-function at all (another account) it and--recordfail; an emptybaseline counts as none (exit 3).
RuntimeVersionConfigis never compared. The integration README says that,once a baseline is committed, a Lambda source edit needs a new record by someone with CI-account access, after a
redeploy or after accepting the deployed files. The download is guarded:
lambda_code.pyin adeployed.jsonthat onlyits owner can read; any other mode is refused (exit 4) before anything is downloaded.
https://URL on the S3 host of a Lambda code bucket (<bucket>-tasks.s3.<region>.amazonaws.com, theshape of
Code.Locationin Lambda's documentation) is fetched. That keeps out other AWS endpoints andpath-style S3. Anyone can name a bucket
…-tasks, so what makes the URL Lambda's is that it comes fromGetFunction. A bracketed host is refused on every python3, not only on those with the 2025urlsplitfixes.--zip-fileuploads) is refused.traceback.
one of the runner's legacy
##[commands.snapshotandlambda-driftrefuse--apply, which would turn their read-only guard off.RuntimeVersionConfig, which AWS changes by itself and which madeverify-unchangedreport a false CHANGED.provision-ci.shchecks the account's headroom only for what a run creates. The AppSync quota can be overridden(
CCIT_CI_APPSYNC_API_QUOTA). Before this, a rerun that only extends the key was refused at 23 APIs.provision-ci.shstops when the custom sender can't be built, and its dry run checks for npm. The guidance itprints for stale uploaded files no longer runs its sentences together.
Integration tests (
CognitoClientHostApp)RequiredMFATestshas two new tests: aUSER_AUTHsign-in with thePASSWORDorPASSWORD_SRPfirst factor thatmust set up TOTP (
MFA_SETUP). From the requests the client sent (RecordingHTTPClient), each checks that thesign-in was one
InitiateAuthUSER_AUTHpreferring that factor, and that the challenges answered wereMFA_SETUP(PASSWORD) orPASSWORD_VERIFIERthenMFA_SETUP(PASSWORD_SRP), since an SRP sign-in reachesthe same steps on that pool. Each test first checks, with one raw
InitiateAuth, that the app client allowsUSER_AUTH. If it doesn't, the test fails, naming the sandbox's and CI's remedies, or skips only under theharness's CI-only skip rule (a new
CISkipReason,mfaRequiredUserAuth). The pool template'sclientandciapp clients now allow
ALLOW_USER_AUTH, withPASSWORDas an allowed first factor.PluginCarriedAndSharedGroupLoginTests(interop target) adds two live checks:deletes it on a user pool change.
.defaultclient are what a relaunchedplugin sees.
client's own
extendedrole, whose senders send no email, so they no longer spend the shared default backend'sdaily email limit.
the interop suite's missing-fixture messages, which now point at CI's file set.
Lambda source edit needs; CI-account maintenance is in the team's internal runbook;
prepare-run.shtolerates;-skipPackagePluginValidationon the guide's host app builds and on the plugin host-app commands, as CI passesit. On a fresh DerivedData the build otherwise stops at validating smithy-swift's code generator plug-in.
Testing
Offline, with every
AWS_*variable unset,AWS_CONFIG_FILEandAWS_SHARED_CREDENTIALS_FILEset to/dev/null,and IMDS off:
actionlinton every changed workflow: each reports only what it reports on main.python3 -m unittest discover -s scripts/python -p 'test_*.py'(12 tests).auth_client_infra_self_tests.ymlruns them:python3 -m unittest discover -s infra -p 'test_*.py'(173 tests:test_parity.py115,test_plugin_configs.py45,test_enroll_totp.py7,test_free_disk.py6);infra/test_*.shandinfra/ci/test_*.sh, includingtest_lambda_code.sh(also on macOS's/usr/bin/python3, 3.9) andtest_provision_ci.shwith its rerun check over the CI resources as firstapplied;
node --teston the Lambda tests (11).scripts/cognito-engine/error_match_gate.sh self-test.swift build --build-tests.bash scripts/cognito-engine/check_fixture_lock.sh HEAD.xcodebuild build-for-testing -skipPackagePluginValidationfor the two client host-app test schemes(
CognitoClientIntegrationTests,CognitoClientPluginInteropTests), with an emptyCOGNITO_CLIENT_INTEG_DIR.Live: on 2026-10-27 the CI account got the new MFA-required pool and its uploaded outputs file, and
verify-unchangedshowed that nothing outsideccit-ci-changed. The client's CI run on this PR passed with noskip: its overlay mapped
ccit-ci-mfa-req-totp-smsonto the MFA-required file, and the zero-skip gate passed. Thenext client CI run confirms the challenge lists the two
USER_AUTHMFA-setup tests now check. The CI-account stepsthat remain are in the team's internal runbook.
The optional sandbox gets the template change (
ALLOW_USER_AUTHand the allowed first factor) on its nextinfra/provision.sh. Until then, the twoUSER_AUTHMFA-setup tests fail there, naming that remedy (a sandbox runnever skips).
Follow-ups
doesn't know, moved into an ignored location such as
scripts/or.github/, selects nothing, while deleting itselects every group. Also pin the selector's default
--repo-root(assert no "found no Xcode project" note).expired key is kept for 60 days and can still be extended (to be checked once on a non-CI API first). The
replacement steps then become the last resort.
aws configure listshows credentials from the profile (no API call).an SMS pool unless the trust admits it, and each Lambda's invoke statement is checked on its own, instead of only
the rerun check that needs a commit outside the public history.
project.pbxprojchange no longer starts the otherscript self-tests (first check that it isn't a required check).
--applyrefuse while any phase-2ccit-ci-resource exists, naming--scope all.provision-ci.sh replace <pool>: the tag checks and deletes the replacement steps list, through thescript's own guards, then the
--scope all --uploadrerun.or a rename's old side) fails detect with an error naming the project, and an edit runs nothing. That covers the
six READMEs only stress-test targets copy, and ends the unit and build workflows' over-selection for them.