Two things to fix. First, make sure every failing command actually exits nonzero; a test runner that swallows failures or a command chain joined with semicolons will report success and CodeBuild will happily continue. Use set -e or && chains in your buildspec commands. Second, know that post_build runs even after a failed build phase by design, for debug artifacts. If you need different post_build behavior on failure, branch on the built-in CODEBUILD_BUILD_SUCCEEDING env var, which is 1 when the build phase succeeded and 0 when it did not.

Context: Stack Overflow #46584324 (accepted answer, 46 votes): a build fails on a test syntax error, yet CodeBuild moves on to the next phase and even produces artifacts. The asker expected execution to stop. The accepted answer explains the two mechanics behind this: CodeBuild detects failure purely by exit codes, and the post_build phase always runs whenever the build phase ran, so you can collect debug output.