Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 69 additions & 1 deletion lib/nopea/deploy.ex
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,8 @@ defmodule Nopea.Deploy do
manifests_applied: result.manifest_count,
duration_ms: result.duration_ms,
verified: result.verified,
error: result.error
error: result.error,
applied_resources: result.applied_resources
}

memory_context =
Expand All @@ -227,6 +228,9 @@ defmodule Nopea.Deploy do

occurrence = Nopea.Occurrence.build(occurrence_input, memory_context)

# Start log emitter and emit key deploy events
occurrence = emit_deploy_logs(occurrence, result)

# Persist to .nopea/ directory
workdir = File.cwd!()
Nopea.Occurrence.persist(occurrence, workdir)
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nopea.Occurrence.persist/2 now uses FalseProtocol.JSON.encode/1, which can return validation errors. generate_occurrence/2 currently ignores the return value of persist/2, so failed persistence can become silent. Consider checking for {:error, reason} here and logging it (or raising) so operators can detect missing occurrences.

Suggested change
Nopea.Occurrence.persist(occurrence, workdir)
case Nopea.Occurrence.persist(occurrence, workdir) do
{:ok, _} ->
:ok
{:error, reason} ->
Logger.error("Failed to persist occurrence",
service: result.service,
deploy_id: result.deploy_id,
reason: inspect(reason)
)
other ->
Logger.error("Unexpected return from Nopea.Occurrence.persist/2",
service: result.service,
deploy_id: result.deploy_id,
return: inspect(other)
)
end

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The entire generate_occurrence/2 is wrapped in a rescue that logs failures. Adding a case match on persist would be redundant — if encoding or writing fails, the rescue catches it and logs service + deploy_id + stacktrace.

Expand All @@ -240,6 +244,70 @@ defmodule Nopea.Deploy do
)
end

defp emit_deploy_logs(occurrence, result) do
case Nopea.Occurrence.start_log_emitter(occurrence) do
{:ok, emitter} ->
FalseProtocol.LogEmitter.info_full(
emitter,
"deploy started for #{result.service}",
%FalseProtocol.Semantic{
event: "deploy.apply.start",
what_happened:
"started applying #{result.manifest_count} manifests to #{result.namespace}"
}
)

emit_status_log(emitter, result)
Nopea.Occurrence.attach_log_ref(occurrence, emitter)

{:error, reason} ->
Logger.warning("Failed to start deploy log emitter",
service: result.service,
reason: inspect(reason)
)

occurrence
end
end

defp emit_status_log(emitter, %{status: :completed} = result) do
FalseProtocol.LogEmitter.info_full(
emitter,
"deploy completed in #{result.duration_ms}ms",
%FalseProtocol.Semantic{
event: "deploy.apply.complete",
what_happened: "#{result.service} deployed successfully",
parameters: %{"verified" => result.verified, "duration_ms" => result.duration_ms}
}
)
end

defp emit_status_log(emitter, %{status: :failed} = result) do
FalseProtocol.LogEmitter.emit(
emitter,
:error,
"deploy failed: #{inspect(result.error)}",
%FalseProtocol.Semantic{
event: "deploy.apply.failed",
what_happened: "#{result.service} deployment failed",
impact: "service in #{result.namespace} is not updated"
}
)
end

defp emit_status_log(emitter, %{status: :rolledback} = result) do
FalseProtocol.LogEmitter.emit(
emitter,
:warning,
"deploy rolledback: #{inspect(result.error)}",
%FalseProtocol.Semantic{
event: "deploy.apply.rolledback",
what_happened: "#{result.service} deployment rolled back",
impact: "service in #{result.namespace} reverted to previous version"
}
)
end

defp emit_start(spec, deploy_id, strategy) do
Nopea.Metrics.emit_deploy_start(%{service: spec.service, strategy: strategy})

Expand Down
Loading
Loading