Each team owns their own microservices. They deploy when ready. Usually this means master is what is deployed. If another team uses that API, then the owner of the service is responsible for maintaining that compatibility until they are able to get the other teams to update their usage of the API.
Looks like I cant reply to you due to comment depth, so I'll reply here.
The reason for being able to push "breaking" changes into the codebase is for future consumption. We control the consumers, which verify that they are able to integrate.
When all consumers have had their contracts updated to be insync with that service, then that service is no longer breaking - even though it hasn't been changed.
Its basically a way of preventing inter-service blockers.
I must admit, I thought these kind of pipelines would have been a lot more commonplace than they appear to be.
They are very complicated pipelines to model. It is simpler if you simply treat other consumers of your services as customers who can't change their code at every whim. They've invested time and money in to your API, and are going to be pissed if you change it all the time.
Obviously this isn't the exact scenario you're in at the moment, but if you're big enough -- this is exactly what it'll be like.
Implement backwards compatible changes and implement tracking on the users of the old features you want to disable. When your tracking shows nobody is using the old features, delete them.
We use a continuous delivery pipeline. This isn't a manual process, but a team isn't going to (by policy) deploy a breaking change which breaks their API for other users of that API.
How do you define what is considered the current version for each microservice in your application?