diff --git a/deploy/homologacao/pipeline.py b/deploy/homologacao/pipeline.py index e7a481b1..bcd84819 100644 --- a/deploy/homologacao/pipeline.py +++ b/deploy/homologacao/pipeline.py @@ -262,6 +262,9 @@ class Delivery: latest=self.git('rev-parse','refs/heads/delivery-target') write_json(self.state/'requests'/(latest+'.json'),{'commit':latest,'reason':'newer integration during build'}) return {'status':'superseded','commit':target,'newer_commit':latest} + # A programmer may have edited the served release while CI was running. + # Recheck immediately before the atomic switch, preserving that work. + refuse_drift(self.current.resolve(),installed['hashes']) previous=promote(self.current,release,lambda:self.health(target)) try: self.run([self.c['php'],str(release/'artisan'),'queue:restart'],cwd=release) @@ -274,15 +277,16 @@ class Delivery: raise return {key:value for key,value in info.items() if key!='hashes'} - def process(self) -> None: + def process(self) -> bool: import fcntl self.state.mkdir(parents=True,exist_ok=True) for directory in ['reports','logs','processed','qa-runtime']: (self.state/directory).mkdir(exist_ok=True) with (self.state/'delivery.lock').open('a') as lock: fcntl.flock(lock,fcntl.LOCK_EX) + succeeded=True for _ in range(3): requests=sorted((self.state/'requests').glob('*.json')) - if not requests:return + if not requests:return succeeded for path in requests: if not SHA.fullmatch(path.stem):raise RuntimeError('Unexpected queue entry') path.rename(self.state/'processed'/(path.stem+'-'+uuid.uuid4().hex[:8]+'.json')) @@ -291,11 +295,13 @@ class Delivery: try: target=self.latest();report=self.deliver(target) except Exception as error: + succeeded=False report={'status':'failed','commit':target,'error':str(error),'active_release_preserved':True} finally:self.log=None report.update(requested_commits=[p.stem for p in requests],finished_at=datetime.datetime.now(datetime.timezone.utc).isoformat()) write_json(self.state/'reports'/((target or 'fetch-failed')+'-'+str(time.time_ns())+'.json'),report) print(json.dumps(report),flush=True) + return succeeded def rollback(self) -> dict: import fcntl @@ -329,6 +335,6 @@ def main(): if Path(config['legacy_root']).resolve()!=Path('/var/www/html/teste-investidor.tempo.co.mz'):raise SystemExit('Unexpected persistent application target') runner=Delivery(config) if args.rollback:print(json.dumps(runner.rollback()),flush=True) - else:runner.process() + else:raise SystemExit(0 if runner.process() else 1) if __name__=='__main__':main()