Skip to content

feat: accept deploy reports from the deployment pipeline - #112

Open
turegjorup wants to merge 4 commits into
developfrom
feature/deploy-results-endpoint
Open

turegjorup wants to merge 4 commits into
developfrom
feature/deploy-results-endpoint

Conversation

@turegjorup

Copy link
Copy Markdown
Contributor

Link to ticket

https://leantime.itkdev.dk/#/tickets/showTicket/7213

Description

Sites deployed from a prebuilt artifact have no .git directory on disk, so the server harvester's git detection finds nothing and posts an empty result. Those sites show up here with no origin and no tag, and with no GitRepo row at all — which is why app:economics:sync-agreements keeps reporting repositories it cannot link, deltag.aarhus.dk among them. This is the DeployDetectionResult item from TODO.md.

The deployment is the only party that knows the repository and tag, so it now reports them to POST /api/deploy_results.

Changes:

  • DeployResult — a new entity and endpoint, following DetectionResult: Post with status: 202, output: false, messenger: true and a written-out openapi block. It is a source record, not derived data, so it is persisted and can be replayed. Typed fields (serverName, rootDir, repoUrl, tag, commit, pipelineUrl) rather than a JSON blob, with Assert constraints that make the documented 422 real.
  • DeployResultHandler resolves the server by name, deduplicates by content hash the way detection results do, and hands off to DeployResultApplier. Routed straight to async: unlike DetectionResult it needs no synchronous hop, because the payload names its own server rather than relying on the security context.
  • DeployResultApplier links the installation to the repository and tag, and is shared with app:replay:deploy-results so a rebuild can restore what deployments reported.
  • Installation::$codeSource (git or artifact) records whether an installation is a git working copy or an unpacked release artifact, shown as a Source column and filter in the admin. It describes what the installation is, not which tool observed it, so it stays true regardless of reporter.
  • GitHandler no longer clears the tag of an artifact installation. The harvester reports an empty git result for those on every scan, and clearing the tag would not merely unlink it — RemovedRelationsListener deletes a git tag, and its repository, as soon as no installation points at it. That is the sharpest edge in this change and it has its own test.
  • GitTagFactory gains resolveGitTag(), extracted so deployments can reuse the repository and tag lookup without the working-copy fields. A deployment has no working tree, so it deliberately writes no gitChanges/gitChangesCountChangesField renders 0 as a badge reading "clean checkout", which a deployment cannot claim.

Authentication uses a key from the environment (APP_WOODPECKER_API_KEY) rather than a row in the database, so rotating it touches no data and can move to the vault later without a code change. It is neither a user nor a server: it grants ROLE_DEPLOYER and unlocks this one endpoint. An unset key never authenticates.

Two behaviours worth calling out, both deliberate:

  • A report naming a server we do not know is accepted and dropped. The report is answered with 202 before the handler runs, so a pipeline is never failed by our bookkeeping. The trade-off is that a mistyped host is invisible on both sides; a log line is the remedy if that turns out to bite.
  • An installation is never created from a deploy report — Installation::$detectionResult is not nullable, and one invented here would be orphan-removed by the next directory scan anyway. A site deployed before its first hourly scan is picked up by the next deployment or by a replay.

Screenshot of the result

Admin change is one added Source column and filter on the Installation list; no screenshot attached as the endpoint itself has no UI.

Checklist

  • My code is covered by test cases.
  • My code passes our test (all our tests).
  • My code passes our static analysis suite.
  • My code passes our continuous integration process.

99 tests, 231 assertions. New coverage: API-level auth and validation (401 / 403 for a server key / 202 for the deploy key / 403 reading /api/servers with it / 422 on a blank tag), the authenticator's unset-key guard, the handler, the applier, GitHandler's guard, and an integration test against a real database asserting the deployed tag and its GitRepo row survive a harvester scan. Both regression tests were confirmed to fail without their fix.

Additional comments or questions

Stacked on #111 — please merge that one first; GitHub will then retarget this to develop. This branch contains its commit, and DeployResultApplier depends on that fix: every redeployment of an already-known tag would otherwise link nothing.

Once this is deployed, the Woodpecker side is itk-devops/woodpecker-ansible-plugin#14, which needs one follow-up commit to send these typed fields instead of the harvester-shaped envelope it currently sends.

addInstallation() was called only when the tag row was created, and it is the
only thing that sets Installation::gitTag. A redeployment, or the same release
on a second server, therefore left the installation pointing at its previous
tag or at nothing - which also dropped it out of the repo advisory rollups,
since those inner join GitRepo through gitTags to installations.
Sites deployed from a prebuilt artifact carry no .git directory, so the server
harvester cannot detect their origin and tag and they show up here with neither
- and with no GitRepo row, which is why the economics sync keeps reporting
repositories it cannot link. The deployment is the only party that knows, so it
now reports to POST /api/deploy_results.

Installations record whether they are a git working copy or a release artifact,
which both tells the two apart in the admin and stops the harvester's empty git
result from clearing a tag only the deployment can know. Clearing it would not
merely unlink the tag: RemovedRelationsListener deletes a git tag, and its
repository, as soon as no installation points at it.

The pipeline authenticates with a key from the environment rather than the
database, so rotating it touches no data and can move to the vault later. The
key grants ROLE_DEPLOYER and nothing else.
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 75.18248% with 34 lines in your changes missing coverage. Please review.
✅ Project coverage is 45.00%. Comparing base (c77a06a) to head (eb1901b).
⚠️ Report is 4 commits behind head on develop.

Files with missing lines Patch % Lines
src/Command/ReplayDeployResultsCommand.php 0.00% 17 Missing ⚠️
src/Entity/DeployResult.php 74.46% 12 Missing ⚠️
src/Service/GitTagFactory.php 73.68% 5 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##             develop     #112      +/-   ##
=============================================
+ Coverage      40.32%   45.00%   +4.68%     
- Complexity      1132     1174      +42     
=============================================
  Files            150      156       +6     
  Lines           3576     3704     +128     
=============================================
+ Hits            1442     1667     +225     
+ Misses          2134     2037      -97     
Flag Coverage Δ
unittests 45.00% <75.18%> (+4.68%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented Sep 16, 2026

Copy link
Copy Markdown

API Specification - Non-breaking changes

API Changelog 1.0.0 vs. 1.0.0

API Changes

POST /api/deploy_results

  • endpoint added

1 similar comment
@github-actions

github-actions Bot commented Sep 16, 2026

Copy link
Copy Markdown

API Specification - Non-breaking changes

API Changelog 1.0.0 vs. 1.0.0

API Changes

POST /api/deploy_results

  • endpoint added

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants