ace-assign 0.71.0 → 0.73.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.ace-defaults/assign/catalog/steps/auto-merge.step.yml +8 -8
- data/.ace-defaults/assign/catalog/steps/create-pr.step.yml +1 -1
- data/.ace-defaults/assign/catalog/steps/mark-pr-ready.step.yml +1 -1
- data/.ace-defaults/assign/config.yml +0 -13
- data/.ace-defaults/assign/presets/fix-bug.yml +1 -1
- data/.ace-defaults/assign/presets/work-on-task-ace-development.yml +1 -1
- data/.ace-defaults/assign/presets/work-on-task-auto-merge.yml +1 -1
- data/.ace-defaults/assign/presets/work-on-task.yml +1 -1
- data/CHANGELOG.md +5 -2116
- data/README.md +58 -31
- data/docs/exit-codes.md +36 -44
- data/docs/getting-started.md +9 -4
- data/docs/usage.md +129 -522
- data/handbook/skills/as-assign-drive/SKILL.md +1 -4
- data/handbook/workflow-instructions/assign/drive.wf.md +47 -195
- data/handbook/workflow-instructions/assign/prepare.wf.md +1 -1
- data/handbook/workflow-instructions/assign/verify-test-suite.wf.md +8 -2
- data/lib/ace/assign/cli/commands/assignment_target.rb +2 -15
- data/lib/ace/assign/cli/commands/create.rb +1 -8
- data/lib/ace/assign/cli/commands/fail.rb +0 -6
- data/lib/ace/assign/cli/commands/finish.rb +2 -23
- data/lib/ace/assign/cli/commands/fork_session.rb +0 -1
- data/lib/ace/assign/cli/commands/select.rb +0 -1
- data/lib/ace/assign/cli/commands/start.rb +0 -2
- data/lib/ace/assign/cli/commands/status.rb +1 -31
- data/lib/ace/assign/cli/commands/step.rb +1 -3
- data/lib/ace/assign/cli.rb +1 -85
- data/lib/ace/assign/errors.rb +8 -80
- data/lib/ace/assign/models/assignment.rb +5 -28
- data/lib/ace/assign/molecules/assignment_manager.rb +0 -26
- data/lib/ace/assign/molecules/fork_session_launcher.rb +4 -12
- data/lib/ace/assign/molecules/lifecycle_exclusion.rb +15 -112
- data/lib/ace/assign/molecules/runtime_control_surface_runner.rb +6 -2
- data/lib/ace/assign/molecules/skill_assign_source_resolver.rb +9 -0
- data/lib/ace/assign/organisms/assignment_executor.rb +0 -6
- data/lib/ace/assign/organisms/task_assignment_creator.rb +3 -8
- data/lib/ace/assign/version.rb +1 -1
- data/lib/ace/assign.rb +0 -6
- metadata +22 -176
- data/.ace-defaults/assign/presets/close-delivery.yml +0 -57
- data/.ace-defaults/assign/presets/deliver-release.yml +0 -60
- data/.ace-defaults/assign/presets/integrate-batch.yml +0 -72
- data/.ace-defaults/assign/presets/supervise-delivery.yml +0 -69
- data/docs/protected-authority.md +0 -282
- data/exe/ace-scope-ready +0 -55
- data/lib/ace/assign/atoms/assignment_scope.rb +0 -58
- data/lib/ace/assign/atoms/attempt_state_machine.rb +0 -84
- data/lib/ace/assign/atoms/candidate_limits.rb +0 -20
- data/lib/ace/assign/atoms/delivery_parameters.rb +0 -19
- data/lib/ace/assign/atoms/evidence_digest.rb +0 -78
- data/lib/ace/assign/atoms/fixed_merge_schemas.rb +0 -398
- data/lib/ace/assign/atoms/git_blob_batch.rb +0 -59
- data/lib/ace/assign/authority/assignment_definition.rb +0 -35
- data/lib/ace/assign/authority/assignment_inventory.rb +0 -290
- data/lib/ace/assign/authority/campaign_consumer_policy.rb +0 -100
- data/lib/ace/assign/authority/campaign_execution.rb +0 -119
- data/lib/ace/assign/authority/campaign_round_transfer.rb +0 -82
- data/lib/ace/assign/authority/candidate_blob_batch.rb +0 -55
- data/lib/ace/assign/authority/candidate_head_comparison.rb +0 -139
- data/lib/ace/assign/authority/candidate_transfer.rb +0 -357
- data/lib/ace/assign/authority/cleanup_result_protection.rb +0 -53
- data/lib/ace/assign/authority/client.rb +0 -257
- data/lib/ace/assign/authority/command_execution.rb +0 -180
- data/lib/ace/assign/authority/deployment.rb +0 -627
- data/lib/ace/assign/authority/deployment_history.rb +0 -146
- data/lib/ace/assign/authority/endcap.rb +0 -457
- data/lib/ace/assign/authority/endcap_attempts.rb +0 -274
- data/lib/ace/assign/authority/endcap_campaign_results.rb +0 -232
- data/lib/ace/assign/authority/endcap_campaign_rounds.rb +0 -266
- data/lib/ace/assign/authority/endcap_contexts.rb +0 -23
- data/lib/ace/assign/authority/endcap_inboxes.rb +0 -37
- data/lib/ace/assign/authority/endcap_prepared_work.rb +0 -80
- data/lib/ace/assign/authority/endcap_publication.rb +0 -41
- data/lib/ace/assign/authority/endcap_results.rb +0 -398
- data/lib/ace/assign/authority/endcap_review_requests.rb +0 -183
- data/lib/ace/assign/authority/endcap_reviews.rb +0 -130
- data/lib/ace/assign/authority/endcap_service_context.rb +0 -38
- data/lib/ace/assign/authority/endcap_service_settlement.rb +0 -152
- data/lib/ace/assign/authority/evidence_transfer.rb +0 -67
- data/lib/ace/assign/authority/execution_scope_observation.rb +0 -903
- data/lib/ace/assign/authority/fixed_publication_lifecycle.rb +0 -176
- data/lib/ace/assign/authority/fixed_service_lifecycle.rb +0 -594
- data/lib/ace/assign/authority/inbox_context_original.rb +0 -113
- data/lib/ace/assign/authority/launch_attempt_consumers.rb +0 -51
- data/lib/ace/assign/authority/launch_campaign_registration.rb +0 -214
- data/lib/ace/assign/authority/launch_control_channel.rb +0 -313
- data/lib/ace/assign/authority/launch_control_exclusions.rb +0 -127
- data/lib/ace/assign/authority/launch_driver.rb +0 -393
- data/lib/ace/assign/authority/launch_input_inhibition.rb +0 -132
- data/lib/ace/assign/authority/launch_lifecycle.rb +0 -997
- data/lib/ace/assign/authority/launch_prepared_work.rb +0 -122
- data/lib/ace/assign/authority/launch_review.rb +0 -91
- data/lib/ace/assign/authority/launch_scope_abort.rb +0 -81
- data/lib/ace/assign/authority/launch_scope_admission.rb +0 -215
- data/lib/ace/assign/authority/launch_scope_close.rb +0 -146
- data/lib/ace/assign/authority/launch_scope_parent.rb +0 -44
- data/lib/ace/assign/authority/launch_scope_release.rb +0 -859
- data/lib/ace/assign/authority/launch_steering.rb +0 -353
- data/lib/ace/assign/authority/launch_stop.rb +0 -254
- data/lib/ace/assign/authority/maintenance_workspace_retirement.rb +0 -229
- data/lib/ace/assign/authority/ordinary_attempt_start.rb +0 -137
- data/lib/ace/assign/authority/ordinary_client.rb +0 -70
- data/lib/ace/assign/authority/ordinary_completion.rb +0 -104
- data/lib/ace/assign/authority/ordinary_effect_settlement.rb +0 -118
- data/lib/ace/assign/authority/ordinary_lifecycle.rb +0 -227
- data/lib/ace/assign/authority/ordinary_merge_evidence.rb +0 -81
- data/lib/ace/assign/authority/ordinary_peer.rb +0 -54
- data/lib/ace/assign/authority/ordinary_progress.rb +0 -162
- data/lib/ace/assign/authority/ordinary_queue.rb +0 -115
- data/lib/ace/assign/authority/ordinary_release_artifacts.rb +0 -143
- data/lib/ace/assign/authority/ordinary_results.rb +0 -206
- data/lib/ace/assign/authority/ordinary_review_evidence.rb +0 -180
- data/lib/ace/assign/authority/ordinary_reviews.rb +0 -214
- data/lib/ace/assign/authority/ordinary_server.rb +0 -75
- data/lib/ace/assign/authority/ordinary_supervisor.rb +0 -99
- data/lib/ace/assign/authority/original_inbox_selection.rb +0 -41
- data/lib/ace/assign/authority/posix_acl.rb +0 -59
- data/lib/ace/assign/authority/prepared_input.rb +0 -130
- data/lib/ace/assign/authority/prepared_queue.rb +0 -185
- data/lib/ace/assign/authority/prepared_task_context.rb +0 -116
- data/lib/ace/assign/authority/prepared_work.rb +0 -309
- data/lib/ace/assign/authority/prepared_worker.rb +0 -69
- data/lib/ace/assign/authority/private_directory.rb +0 -33
- data/lib/ace/assign/authority/protected_assignment_context.rb +0 -226
- data/lib/ace/assign/authority/readiness_hook.rb +0 -120
- data/lib/ace/assign/authority/receipt_transfer.rb +0 -48
- data/lib/ace/assign/authority/release_artifact_manifest.rb +0 -91
- data/lib/ace/assign/authority/retained_input.rb +0 -53
- data/lib/ace/assign/authority/router.rb +0 -81
- data/lib/ace/assign/authority/server.rb +0 -131
- data/lib/ace/assign/authority/service_cleanup_dispatch.rb +0 -88
- data/lib/ace/assign/authority/service_cleanup_evidence.rb +0 -180
- data/lib/ace/assign/authority/service_delivery_evidence.rb +0 -101
- data/lib/ace/assign/authority/service_evidence.rb +0 -286
- data/lib/ace/assign/authority/service_publication_evidence.rb +0 -110
- data/lib/ace/assign/authority/socket_server.rb +0 -255
- data/lib/ace/assign/authority/task_context_entry.rb +0 -32
- data/lib/ace/assign/authority/transfer_codec.rb +0 -189
- data/lib/ace/assign/cli/commands/attempt/base.rb +0 -84
- data/lib/ace/assign/cli/commands/attempt/evidence.rb +0 -28
- data/lib/ace/assign/cli/commands/attempt/finish.rb +0 -51
- data/lib/ace/assign/cli/commands/attempt/reconcile.rb +0 -49
- data/lib/ace/assign/cli/commands/attempt/start.rb +0 -45
- data/lib/ace/assign/cli/commands/attempt/status.rb +0 -54
- data/lib/ace/assign/cli/commands/authority/inbox_context_principal.rb +0 -25
- data/lib/ace/assign/cli/commands/authority/inbox_context_selection.rb +0 -28
- data/lib/ace/assign/cli/commands/authority/launch.rb +0 -97
- data/lib/ace/assign/cli/commands/authority/serve.rb +0 -31
- data/lib/ace/assign/cli/commands/authority/status.rb +0 -26
- data/lib/ace/assign/cli/commands/authority/task_context.rb +0 -31
- data/lib/ace/assign/cli/commands/authority/task_context_principal.rb +0 -25
- data/lib/ace/assign/cli/commands/authority/task_context_selection.rb +0 -30
- data/lib/ace/assign/cli/commands/authority/terminate.rb +0 -29
- data/lib/ace/assign/cli/commands/authority/worker.rb +0 -23
- data/lib/ace/assign/cli/commands/campaign_export_result.rb +0 -55
- data/lib/ace/assign/cli/commands/campaign_record_round.rb +0 -57
- data/lib/ace/assign/cli/commands/delivery.rb +0 -107
- data/lib/ace/assign/cli/commands/inbox_bind.rb +0 -34
- data/lib/ace/assign/cli/commands/protected_submission.rb +0 -61
- data/lib/ace/assign/cli/commands/resume.rb +0 -38
- data/lib/ace/assign/cli/commands/submit_candidate.rb +0 -34
- data/lib/ace/assign/cli/commands/submit_result.rb +0 -61
- data/lib/ace/assign/models/attempt.rb +0 -178
- data/lib/ace/assign/models/attempt_binding.rb +0 -116
- data/lib/ace/assign/models/evidence_event.rb +0 -86
- data/lib/ace/assign/models/execution_receipt.rb +0 -139
- data/lib/ace/assign/molecules/attempt_reconciler.rb +0 -127
- data/lib/ace/assign/molecules/attempt_store.rb +0 -201
- data/lib/ace/assign/molecules/canonical_attempt_state.rb +0 -151
- data/lib/ace/assign/molecules/canonical_evidence.rb +0 -170
- data/lib/ace/assign/molecules/canonical_read_commands.rb +0 -84
- data/lib/ace/assign/molecules/canonical_read_snapshot.rb +0 -313
- data/lib/ace/assign/molecules/evidence_calculator.rb +0 -237
- data/lib/ace/assign/molecules/evidence_journal.rb +0 -1899
- data/lib/ace/assign/molecules/execution_identity_resolver.rb +0 -137
- data/lib/ace/assign/molecules/execution_scope_lineage.rb +0 -345
- data/lib/ace/assign/molecules/fixed_effect_reference.rb +0 -31
- data/lib/ace/assign/molecules/installed_journal_read.rb +0 -143
- data/lib/ace/assign/molecules/journal_input_inhibition.rb +0 -78
- data/lib/ace/assign/molecules/journal_mutation.rb +0 -274
- data/lib/ace/assign/molecules/journal_prompt_mutation.rb +0 -241
- data/lib/ace/assign/molecules/ordinary_merge_cli.rb +0 -68
- data/lib/ace/assign/molecules/ordinary_merge_contract.rb +0 -410
- data/lib/ace/assign/molecules/original_launch_binding.rb +0 -37
- data/lib/ace/assign/molecules/proposal_journal.rb +0 -179
- data/lib/ace/assign/molecules/protected_control_exclusion.rb +0 -303
- data/lib/ace/assign/molecules/protected_workspace_exclusion.rb +0 -330
- data/lib/ace/assign/molecules/protected_workspace_provisioner.rb +0 -164
- data/lib/ace/assign/molecules/receipt_verifier.rb +0 -320
- data/lib/ace/assign/molecules/terminal_scope_receipt.rb +0 -88
- data/lib/ace/assign/organisms/attempt_coordinator.rb +0 -1374
- data/lib/ace/assign/organisms/delivery_coordinator.rb +0 -342
- data/lib/ace/assign/organisms/ordinary_driver.rb +0 -310
- data/lib/ace/assign/organisms/ordinary_merge_consumer.rb +0 -365
- data/lib/ace/assign/organisms/prepared_work_builder.rb +0 -194
- data/lib/ace/assign/organisms/protected_delivery_coordinator.rb +0 -100
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require_relative "base"
|
|
3
|
-
module Ace
|
|
4
|
-
module Assign
|
|
5
|
-
module CLI
|
|
6
|
-
module Commands
|
|
7
|
-
module Attempt
|
|
8
|
-
class Evidence < Ace::Support::Cli::Command
|
|
9
|
-
include Attempt::Base
|
|
10
|
-
desc "Read coordinator-accepted live execution or historical execution authority"
|
|
11
|
-
option :attempt, required: true, desc: "Accepted execution attempt ID"
|
|
12
|
-
option :receipt_digest, required: true, desc: "Accepted receipt digest"
|
|
13
|
-
option :kind, default: "check", desc: "Evidence purpose: check, review-collection or review-approval"
|
|
14
|
-
option :check_name, default: "tests", desc: "Required check name (tests binds operation test)"
|
|
15
|
-
option :historical_head, desc: "Exact recorded head for historical authority only (never current-head evidence)"
|
|
16
|
-
option :format, default: "json", desc: "JSON evidence projection"
|
|
17
|
-
|
|
18
|
-
def call(**options)
|
|
19
|
-
raise Ace::Support::Cli::Error, "evidence supports --format json" unless options[:format] == "json"
|
|
20
|
-
emit_json(build_coordinator.evidence(attempt_id: options[:attempt], receipt_digest: options[:receipt_digest],
|
|
21
|
-
kind: options[:kind], check_name: options[:check_name], historical_head: options[:historical_head]))
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "json"
|
|
4
|
-
require_relative "base"
|
|
5
|
-
|
|
6
|
-
module Ace
|
|
7
|
-
module Assign
|
|
8
|
-
module CLI
|
|
9
|
-
module Commands
|
|
10
|
-
module Attempt
|
|
11
|
-
# Protected completion selects an already submitted canonical result.
|
|
12
|
-
# Ordinary local completion accepts its structured receipt file.
|
|
13
|
-
class Finish < Ace::Support::Cli::Command
|
|
14
|
-
include Ace::Support::Cli::Base
|
|
15
|
-
include Attempt::Base
|
|
16
|
-
|
|
17
|
-
desc "Finish a protected canonical result or an ordinary local receipt"
|
|
18
|
-
|
|
19
|
-
option :attempt, desc: "Attempt ID"
|
|
20
|
-
option :mapping, desc: "Exact installed protected mapping ID"
|
|
21
|
-
option :assignment, desc: "Exact protected assignment ID"
|
|
22
|
-
option :mutation, desc: "Stable original authority mutation ID"
|
|
23
|
-
option :expected_generation, desc: "Original authority mutation generation"
|
|
24
|
-
option :receipt, desc: "Path to the ordinary local receipt JSON file"
|
|
25
|
-
option :result, desc: "Already submitted canonical protected result ID"
|
|
26
|
-
option :head, desc: "Exact canonical candidate head"
|
|
27
|
-
option :candidate_generation, desc: "Original canonical candidate generation"
|
|
28
|
-
|
|
29
|
-
def call(**options)
|
|
30
|
-
if (context = protected_context(options))
|
|
31
|
-
raise Ace::Support::Cli::Error, "Protected finish forbids --receipt" unless options[:receipt].to_s.empty?
|
|
32
|
-
usage = "finish --mapping MAP --assignment ID --attempt ID --result ID --head SHA --candidate-generation N --mutation ID --expected-generation N"
|
|
33
|
-
client, params, mutation = protected_attempt_request(context, options, usage)
|
|
34
|
-
params.merge!("result_id" => require_option(options, :result, usage), "head" => require_option(options, :head, usage),
|
|
35
|
-
"candidate_generation" => integer_option(options, :candidate_generation, usage, positive: true))
|
|
36
|
-
emit_json(protected_call(client, "finish", params, mutation))
|
|
37
|
-
return
|
|
38
|
-
end
|
|
39
|
-
attempt_id = require_option(options, :attempt, "finish --attempt ID --receipt FILE")
|
|
40
|
-
receipt = require_option(options, :receipt, "finish --attempt ID --receipt FILE")
|
|
41
|
-
|
|
42
|
-
result = build_coordinator.finish(attempt_id: attempt_id, receipt_path: receipt)
|
|
43
|
-
|
|
44
|
-
emit_json(result.projection)
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "json"
|
|
4
|
-
require_relative "base"
|
|
5
|
-
|
|
6
|
-
module Ace
|
|
7
|
-
module Assign
|
|
8
|
-
module CLI
|
|
9
|
-
module Commands
|
|
10
|
-
module Attempt
|
|
11
|
-
# Classify interruptions and resolve uncertainty against verified,
|
|
12
|
-
# boundary-attributed receipts. Never replays external effects.
|
|
13
|
-
class Reconcile < Ace::Support::Cli::Command
|
|
14
|
-
include Ace::Support::Cli::Base
|
|
15
|
-
include Attempt::Base
|
|
16
|
-
|
|
17
|
-
desc "Reconcile an interrupted or uncertain attempt"
|
|
18
|
-
|
|
19
|
-
option :attempt, desc: "Attempt ID"
|
|
20
|
-
option :mapping, desc: "Exact installed protected mapping ID"
|
|
21
|
-
option :assignment, desc: "Exact protected assignment ID"
|
|
22
|
-
option :mutation, desc: "Stable original authority mutation ID"
|
|
23
|
-
option :expected_generation, desc: "Original authority mutation generation"
|
|
24
|
-
option :receipt, desc: "Path to the receipt JSON file (required to resolve uncertainty)"
|
|
25
|
-
|
|
26
|
-
def call(**options)
|
|
27
|
-
if (context = protected_context(options))
|
|
28
|
-
raise Ace::Support::Cli::Error, "Protected recovery forbids --receipt" unless options[:receipt].to_s.empty?
|
|
29
|
-
usage = "reconcile --mapping MAP --assignment ID --attempt ID --mutation ID --expected-generation N"
|
|
30
|
-
client, params, mutation = protected_attempt_request(context, options, usage)
|
|
31
|
-
emit_json(protected_call(client, "recover", params, mutation))
|
|
32
|
-
return
|
|
33
|
-
end
|
|
34
|
-
attempt_id = require_option(options, :attempt, "reconcile --attempt ID [--receipt FILE]")
|
|
35
|
-
receipt = options[:receipt].to_s.strip
|
|
36
|
-
|
|
37
|
-
result = build_coordinator.reconcile(
|
|
38
|
-
attempt_id: attempt_id,
|
|
39
|
-
receipt_path: receipt.empty? ? nil : receipt
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
emit_json(result.projection)
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
end
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "json"
|
|
4
|
-
require_relative "base"
|
|
5
|
-
|
|
6
|
-
module Ace
|
|
7
|
-
module Assign
|
|
8
|
-
module CLI
|
|
9
|
-
module Commands
|
|
10
|
-
module Attempt
|
|
11
|
-
# Bind an immutable attempt to an assignment step/subtree.
|
|
12
|
-
#
|
|
13
|
-
# Identity is derived from the execution boundary; a repeated
|
|
14
|
-
# identical start returns the active attempt, and a conflicting
|
|
15
|
-
# binding fails without launching a second writer.
|
|
16
|
-
class Start < Ace::Support::Cli::Command
|
|
17
|
-
include Ace::Support::Cli::Base
|
|
18
|
-
include Attempt::Base
|
|
19
|
-
|
|
20
|
-
desc "Start a scoped attempt for an assignment step"
|
|
21
|
-
|
|
22
|
-
option :assignment, desc: "Assignment ID"
|
|
23
|
-
option :step, desc: "Step or subtree scope (e.g. 010 or 010.01)"
|
|
24
|
-
option :project, desc: "Project ID the attempt executes in"
|
|
25
|
-
|
|
26
|
-
def call(**options)
|
|
27
|
-
usage = "start --assignment ID --step STEP --project ID"
|
|
28
|
-
assignment = require_option(options, :assignment, usage)
|
|
29
|
-
step = require_option(options, :step, usage)
|
|
30
|
-
project = require_option(options, :project, usage)
|
|
31
|
-
|
|
32
|
-
attempt = build_coordinator.start(
|
|
33
|
-
assignment_id: assignment,
|
|
34
|
-
step: step,
|
|
35
|
-
project_id: project
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
emit_json(attempt.projection)
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "json"
|
|
4
|
-
require_relative "base"
|
|
5
|
-
|
|
6
|
-
module Ace
|
|
7
|
-
module Assign
|
|
8
|
-
module CLI
|
|
9
|
-
module Commands
|
|
10
|
-
module Attempt
|
|
11
|
-
# Show attempt state, binding facts, and evidence references.
|
|
12
|
-
# JSON output carries only state, binding, references, and digests
|
|
13
|
-
# — never credentials or receipt bytes.
|
|
14
|
-
class Status < Ace::Support::Cli::Command
|
|
15
|
-
include Ace::Support::Cli::Base
|
|
16
|
-
include Attempt::Base
|
|
17
|
-
|
|
18
|
-
desc "Show attempt status for an assignment"
|
|
19
|
-
|
|
20
|
-
option :assignment, desc: "Assignment ID"
|
|
21
|
-
option :format, desc: "Output format (json, text)", default: "json"
|
|
22
|
-
|
|
23
|
-
def call(**options)
|
|
24
|
-
assignment = require_option(options, :assignment, "status --assignment ID --format json")
|
|
25
|
-
projection = build_coordinator.status(assignment)
|
|
26
|
-
|
|
27
|
-
if options[:format] == "text"
|
|
28
|
-
print_text(assignment, projection)
|
|
29
|
-
else
|
|
30
|
-
emit_json(projection || {"attempt" => nil})
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
private
|
|
35
|
-
|
|
36
|
-
def print_text(assignment, projection)
|
|
37
|
-
return puts "No attempts recorded for #{assignment}" if projection.nil?
|
|
38
|
-
|
|
39
|
-
puts "Attempt: #{projection['attempt_id']} #{projection['state']} (#{projection['scope']})"
|
|
40
|
-
puts "Recovery: #{projection['recovery_mode']}"
|
|
41
|
-
puts "Base head: #{projection['base_head']}"
|
|
42
|
-
puts "Candidate head: #{projection['candidate_head'] || 'unpinned'}"
|
|
43
|
-
puts "Evidence ref: #{projection['evidence_git_ref'] || 'local-only'}"
|
|
44
|
-
puts "Journal commit: #{projection['journal_commit'] || 'none'}"
|
|
45
|
-
return unless projection["unresolved_effects"].any?
|
|
46
|
-
|
|
47
|
-
puts "Unresolved effects: #{projection['unresolved_effects'].join(', ')}"
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require_relative "../../../authority/prepared_task_context"
|
|
3
|
-
|
|
4
|
-
module Ace
|
|
5
|
-
module Assign
|
|
6
|
-
module CLI
|
|
7
|
-
module Commands
|
|
8
|
-
module Authority
|
|
9
|
-
class InboxContextPrincipal < Ace::Support::Cli::Command
|
|
10
|
-
desc "Classify the actual caller through every retained protected owner"
|
|
11
|
-
|
|
12
|
-
def call(**options)
|
|
13
|
-
raise ArgumentError, "fixed inbox context options differ" unless options.empty?
|
|
14
|
-
consumer = Ace::Assign::Authority::PreparedTaskContext.new(context: @protected_assignment_context)
|
|
15
|
-
$stdout.write(consumer.inbox_principal())
|
|
16
|
-
0
|
|
17
|
-
rescue ArgumentError, Ace::Runtime::Error, Ace::Assign::Error => error
|
|
18
|
-
raise Ace::Support::Cli::Error, error.message
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require_relative "../../../authority/prepared_task_context"
|
|
3
|
-
|
|
4
|
-
module Ace
|
|
5
|
-
module Assign
|
|
6
|
-
module CLI
|
|
7
|
-
module Commands
|
|
8
|
-
module Authority
|
|
9
|
-
class InboxContextSelection < Ace::Support::Cli::Command
|
|
10
|
-
desc "Read the fixed installed inbox context selection"
|
|
11
|
-
option :project, required: true, desc: "Installed project ID"
|
|
12
|
-
option :mapping, required: true, desc: "Installed mapping ID"
|
|
13
|
-
option :inbox_context, required: true, desc: "Installed inbox context ID"
|
|
14
|
-
|
|
15
|
-
def call(**options)
|
|
16
|
-
raise ArgumentError, "fixed inbox context options differ" unless options.keys.sort == %i[inbox_context mapping project]
|
|
17
|
-
consumer = Ace::Assign::Authority::PreparedTaskContext.new(context: @protected_assignment_context)
|
|
18
|
-
$stdout.write(consumer.inbox_selection(**options))
|
|
19
|
-
0
|
|
20
|
-
rescue ArgumentError, Ace::Runtime::Error, Ace::Assign::Error => error
|
|
21
|
-
raise Ace::Support::Cli::Error, error.message
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require "json"
|
|
3
|
-
require_relative "../../../authority/launch_driver"
|
|
4
|
-
module Ace
|
|
5
|
-
module Assign
|
|
6
|
-
module CLI
|
|
7
|
-
module Commands
|
|
8
|
-
module Authority
|
|
9
|
-
class Launch < Ace::Support::Cli::Command
|
|
10
|
-
desc "Launch one mapped worker and retain its original foreground control channel"
|
|
11
|
-
READY_FIELDS = %w[assignment_id attempt_id generation journal_commit mapping_id original_binding_digest type version].freeze
|
|
12
|
-
READY_LIMIT = 16_384
|
|
13
|
-
option :mapping, required: true, desc: "Installed launch mapping ID"
|
|
14
|
-
option :assignment, desc: "Managed assignment ID"
|
|
15
|
-
option :definition, desc: "Retained final prepared assignment definition JSON file"
|
|
16
|
-
option :prepared_bundle, desc: "Retained original prepared input Git bundle"
|
|
17
|
-
option :step, desc: "Assignment subtree scope"
|
|
18
|
-
option :base_head, desc: "Exact base commit SHA"
|
|
19
|
-
option :mutation, desc: "Stable invocation ID for safe replay"
|
|
20
|
-
option :dry_run, type: :boolean, default: false, desc: "Verify installed boundary without mutation or creation"
|
|
21
|
-
def call(**options)
|
|
22
|
-
driver = build_driver(options.fetch(:mapping))
|
|
23
|
-
result = if options[:dry_run]
|
|
24
|
-
driver.preflight
|
|
25
|
-
else
|
|
26
|
-
%i[assignment definition prepared_bundle step base_head].each do |key|
|
|
27
|
-
raise ArgumentError, "Missing --#{key.to_s.tr('_', '-')}" unless options[key].is_a?(String) && !options[key].empty?
|
|
28
|
-
end
|
|
29
|
-
definition = retained_input(options[:definition], 32_768).force_encoding(Encoding::UTF_8)
|
|
30
|
-
raise ArgumentError, "assignment definition is not UTF-8" unless definition.valid_encoding?
|
|
31
|
-
bundle = retained_input(options[:prepared_bundle], Ace::Assign::Authority::CandidateTransfer::MAX_BYTES)
|
|
32
|
-
args = {prepared_bundle: bundle, assignment_id: options[:assignment], definition_bytes: definition,
|
|
33
|
-
scope: options[:step], base_head: options[:base_head]}
|
|
34
|
-
args[:mutation_id] = options[:mutation] if options[:mutation]
|
|
35
|
-
driver.launch(**args)
|
|
36
|
-
end
|
|
37
|
-
if !options[:dry_run] && result["phase"] == "issued"
|
|
38
|
-
retaining_control = true
|
|
39
|
-
announced = false
|
|
40
|
-
driver.serve_control!(state: result) do |ready|
|
|
41
|
-
raise ArgumentError, "original launch readiness was already announced" if announced
|
|
42
|
-
line = readiness_line(ready, result, options)
|
|
43
|
-
$stdout.write(line)
|
|
44
|
-
$stdout.flush
|
|
45
|
-
announced = true
|
|
46
|
-
end
|
|
47
|
-
raise ArgumentError, "original control ended before authenticated readiness" unless announced
|
|
48
|
-
else
|
|
49
|
-
puts JSON.generate(result)
|
|
50
|
-
end
|
|
51
|
-
rescue ArgumentError, SystemCallError, Ace::Runtime::Error, Ace::Assign::Error => error
|
|
52
|
-
raise Ace::Support::Cli::Error, error.message
|
|
53
|
-
ensure
|
|
54
|
-
# Local foreground-loop cancellation is not a stopped/settled proof.
|
|
55
|
-
driver.request_control_cancel if retaining_control
|
|
56
|
-
end
|
|
57
|
-
private
|
|
58
|
-
def retained_input(path, limit)
|
|
59
|
-
File.open(path, File::RDONLY | File::NOFOLLOW | File::NONBLOCK) do |file|
|
|
60
|
-
before = file.stat
|
|
61
|
-
unless before.file? && before.size.between?(1, limit)
|
|
62
|
-
raise ArgumentError, "retained launch input is not a bounded regular file"
|
|
63
|
-
end
|
|
64
|
-
bytes = (file.read(limit + 1) || "").b
|
|
65
|
-
after = file.stat
|
|
66
|
-
identity = ->(stat) { [stat.dev, stat.ino, stat.size, stat.mtime, stat.ctime] }
|
|
67
|
-
unless bytes.bytesize == before.size && bytes.bytesize <= limit && identity.call(before) == identity.call(after)
|
|
68
|
-
raise ArgumentError, "retained launch input changed while reading"
|
|
69
|
-
end
|
|
70
|
-
bytes
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def readiness_line(ready, result, options)
|
|
75
|
-
unless ready.is_a?(Hash) && ready.keys.all? { |key| key.is_a?(String) } && ready.keys.sort == READY_FIELDS &&
|
|
76
|
-
ready["version"].is_a?(Integer) && ready["version"] == 1 && ready["type"] == "launch_ready" &&
|
|
77
|
-
ready.values_at("mapping_id", "assignment_id", "attempt_id") ==
|
|
78
|
-
[options.fetch(:mapping), options.fetch(:assignment), result.fetch("attempt_id")] &&
|
|
79
|
-
ready["generation"].is_a?(Integer) && ready["generation"].positive? &&
|
|
80
|
-
ready["journal_commit"].is_a?(String) && ready["journal_commit"].match?(/\A(?:[0-9a-f]{40}|[0-9a-f]{64})\z/) &&
|
|
81
|
-
ready["original_binding_digest"].is_a?(String) && ready["original_binding_digest"].match?(/\A[0-9a-f]{64}\z/)
|
|
82
|
-
raise ArgumentError, "original launch readiness is malformed or mismatched"
|
|
83
|
-
end
|
|
84
|
-
line = JSON.generate(ready) + "\n"
|
|
85
|
-
raise ArgumentError, "original launch readiness is oversized" if line.bytesize > READY_LIMIT
|
|
86
|
-
line
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def build_driver(mapping)
|
|
90
|
-
Ace::Assign::Authority::LaunchDriver.new(mapping_id: mapping)
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
end
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require_relative "../../../authority/server"
|
|
3
|
-
require_relative "../../../authority/router"
|
|
4
|
-
require_relative "../../../authority/launch_lifecycle"
|
|
5
|
-
module Ace
|
|
6
|
-
module Assign
|
|
7
|
-
module CLI
|
|
8
|
-
module Commands
|
|
9
|
-
module Authority
|
|
10
|
-
class Serve < Ace::Support::Cli::Command
|
|
11
|
-
desc "Serve the installed protected assignment authority"
|
|
12
|
-
option :authority, required: true, desc: "Installed authority ID"
|
|
13
|
-
def call(**options)
|
|
14
|
-
deployment = Ace::Assign::Authority::Deployment.load
|
|
15
|
-
history = Ace::Assign::Authority::DeploymentHistory.load
|
|
16
|
-
launch = Ace::Assign::Authority::LaunchLifecycle.new(deployment: deployment, deployment_history: history)
|
|
17
|
-
router = Ace::Assign::Authority::Router.new(launch: launch)
|
|
18
|
-
server = Ace::Assign::Authority::Server.new(authority_id: options.fetch(:authority), deployment: deployment, lifecycle: router)
|
|
19
|
-
signals = %w[INT TERM].to_h { |signal| [signal, Signal.trap(signal) { server.request_stop }] }
|
|
20
|
-
server.serve
|
|
21
|
-
rescue ArgumentError, Ace::Runtime::Error, Ace::Assign::Error => error
|
|
22
|
-
raise Ace::Support::Cli::Error, error.message
|
|
23
|
-
ensure
|
|
24
|
-
signals&.each { |signal, handler| Signal.trap(signal, handler) }
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require "json"
|
|
3
|
-
require_relative "../../../authority/client"
|
|
4
|
-
module Ace
|
|
5
|
-
module Assign
|
|
6
|
-
module CLI
|
|
7
|
-
module Commands
|
|
8
|
-
module Authority
|
|
9
|
-
class Status < Ace::Support::Cli::Command
|
|
10
|
-
desc "Inspect canonical protected launch ownership"
|
|
11
|
-
option :mapping, required: true, desc: "Installed launch mapping ID"
|
|
12
|
-
option :assignment, required: true, desc: "Assignment ID"
|
|
13
|
-
option :attempt, required: true, desc: "Attempt ID"
|
|
14
|
-
def call(**options)
|
|
15
|
-
reply = Ace::Assign::Authority::Client.new(mapping_id: options.fetch(:mapping)).call("attempt_status",
|
|
16
|
-
{"assignment_id" => options.fetch(:assignment), "attempt_id" => options.fetch(:attempt), "result_candidate_generation" => nil})
|
|
17
|
-
puts JSON.generate(reply.data)
|
|
18
|
-
rescue ArgumentError, Ace::Runtime::Error, Ace::Assign::Error => error
|
|
19
|
-
raise Ace::Support::Cli::Error, error.message
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require_relative "../../../authority/prepared_task_context"
|
|
3
|
-
|
|
4
|
-
module Ace
|
|
5
|
-
module Assign
|
|
6
|
-
module CLI
|
|
7
|
-
module Commands
|
|
8
|
-
module Authority
|
|
9
|
-
class TaskContext < Ace::Support::Cli::Command
|
|
10
|
-
desc "Return the captured original task context through the fixed selected entry"
|
|
11
|
-
option :mapping, required: true, desc: "Installed original mapping ID"
|
|
12
|
-
option :assignment, required: true, desc: "Original assignment ID@SCOPE"
|
|
13
|
-
option :attempt, required: true, desc: "Original protected attempt ID"
|
|
14
|
-
option :task, required: true, desc: "Captured canonical task ID"
|
|
15
|
-
|
|
16
|
-
def call(**options)
|
|
17
|
-
unless options.keys.sort == %i[assignment attempt mapping task]
|
|
18
|
-
raise ArgumentError, "fixed task-context accepts only its four selectors"
|
|
19
|
-
end
|
|
20
|
-
consumer = Ace::Assign::Authority::PreparedTaskContext.new(context: @protected_assignment_context)
|
|
21
|
-
$stdout.write(consumer.call(**options))
|
|
22
|
-
0
|
|
23
|
-
rescue ArgumentError, Ace::Runtime::Error, Ace::Assign::Error => error
|
|
24
|
-
raise Ace::Support::Cli::Error, error.message
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require_relative "../../../authority/prepared_task_context"
|
|
3
|
-
|
|
4
|
-
module Ace
|
|
5
|
-
module Assign
|
|
6
|
-
module CLI
|
|
7
|
-
module Commands
|
|
8
|
-
module Authority
|
|
9
|
-
class TaskContextPrincipal < Ace::Support::Cli::Command
|
|
10
|
-
desc "Classify this actual caller through the installed and retained protected owners"
|
|
11
|
-
|
|
12
|
-
def call(**options)
|
|
13
|
-
raise ArgumentError, "fixed task-context-principal accepts no options" unless options.empty?
|
|
14
|
-
consumer = Ace::Assign::Authority::PreparedTaskContext.new(context: @protected_assignment_context)
|
|
15
|
-
$stdout.write(consumer.principal)
|
|
16
|
-
0
|
|
17
|
-
rescue ArgumentError, Ace::Runtime::Error, Ace::Assign::Error => error
|
|
18
|
-
raise Ace::Support::Cli::Error, error.message
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require_relative "../../../authority/prepared_task_context"
|
|
3
|
-
|
|
4
|
-
module Ace
|
|
5
|
-
module Assign
|
|
6
|
-
module CLI
|
|
7
|
-
module Commands
|
|
8
|
-
module Authority
|
|
9
|
-
class TaskContextSelection < Ace::Support::Cli::Command
|
|
10
|
-
desc "Return the authenticated original task-context entry without captured text"
|
|
11
|
-
option :mapping, required: true, desc: "Installed original mapping ID"
|
|
12
|
-
option :assignment, required: true, desc: "Original assignment ID@SCOPE"
|
|
13
|
-
option :attempt, required: true, desc: "Original protected attempt ID"
|
|
14
|
-
|
|
15
|
-
def call(**options)
|
|
16
|
-
unless options.keys.sort == %i[assignment attempt mapping]
|
|
17
|
-
raise ArgumentError, "fixed task-context-selection accepts only its three selectors"
|
|
18
|
-
end
|
|
19
|
-
consumer = Ace::Assign::Authority::PreparedTaskContext.new(context: @protected_assignment_context)
|
|
20
|
-
$stdout.write(consumer.selection(**options))
|
|
21
|
-
0
|
|
22
|
-
rescue ArgumentError, Ace::Runtime::Error, Ace::Assign::Error => error
|
|
23
|
-
raise Ace::Support::Cli::Error, error.message
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require "json"
|
|
3
|
-
require_relative "../../../authority/launch_driver"
|
|
4
|
-
module Ace
|
|
5
|
-
module Assign
|
|
6
|
-
module CLI
|
|
7
|
-
module Commands
|
|
8
|
-
module Authority
|
|
9
|
-
class Terminate < Ace::Support::Cli::Command
|
|
10
|
-
desc "Close the original gated native child and request proven abort"
|
|
11
|
-
option :mapping, required: true, desc: "Installed launch mapping ID"
|
|
12
|
-
option :assignment, required: true, desc: "Assignment ID"
|
|
13
|
-
option :attempt, required: true, desc: "Attempt ID"
|
|
14
|
-
def call(**options)
|
|
15
|
-
client = Ace::Assign::Authority::Client.new(mapping_id: options.fetch(:mapping))
|
|
16
|
-
state = client.call("inspect_launch", {"assignment_id" => options.fetch(:assignment), "attempt_id" => options.fetch(:attempt)}).data
|
|
17
|
-
binding = state.fetch("process_binding") { raise ArgumentError, "no positively recorded original child; supervisor inspection required" }
|
|
18
|
-
result = Ace::Assign::Authority::LaunchDriver.new(mapping_id: options.fetch(:mapping)).terminate(
|
|
19
|
-
state: state, binding: binding, evidence: "protected exact-child termination requested")
|
|
20
|
-
puts JSON.generate(result)
|
|
21
|
-
rescue ArgumentError, Ace::Runtime::Error, Ace::Assign::Error => error
|
|
22
|
-
raise Ace::Support::Cli::Error, error.message
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require_relative "../../../authority/prepared_worker"
|
|
3
|
-
|
|
4
|
-
module Ace
|
|
5
|
-
module Assign
|
|
6
|
-
module CLI
|
|
7
|
-
module Commands
|
|
8
|
-
module Authority
|
|
9
|
-
class Worker < Ace::Support::Cli::Command
|
|
10
|
-
desc "Consume the original authenticated prepared subtree as its fixed worker"
|
|
11
|
-
|
|
12
|
-
def call(**options)
|
|
13
|
-
raise ArgumentError, "fixed worker accepts no options" unless options.empty?
|
|
14
|
-
Ace::Assign::Authority::PreparedWorker.new.run
|
|
15
|
-
rescue ArgumentError, Ace::Runtime::Error, Ace::Assign::Error => error
|
|
16
|
-
raise Ace::Support::Cli::Error, error.message
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require_relative "protected_submission"
|
|
3
|
-
|
|
4
|
-
module Ace
|
|
5
|
-
module Assign
|
|
6
|
-
module CLI
|
|
7
|
-
module Commands
|
|
8
|
-
class CampaignExportResult < Ace::Support::Cli::Command
|
|
9
|
-
include Ace::Support::Cli::Base
|
|
10
|
-
include ProtectedSubmission
|
|
11
|
-
desc "Download a verified accepted campaign result through its original authority"
|
|
12
|
-
option :mapping, desc: "Original installed parent mapping"
|
|
13
|
-
option :assignment, desc: "Original parent assignment"
|
|
14
|
-
option :attempt, desc: "Original parent attempt"
|
|
15
|
-
option :head, desc: "Exact parent candidate head"
|
|
16
|
-
option :candidate_generation, desc: "Exact parent candidate counter"
|
|
17
|
-
option :output, desc: "Result byte file (created once, exact retries allowed)"
|
|
18
|
-
|
|
19
|
-
def call(**options)
|
|
20
|
-
usage = "campaign-export-result with original parent selectors and --output FILE"
|
|
21
|
-
context = protected_context(options)
|
|
22
|
-
raise AttemptErrors::EvidenceUnavailable, "Campaign export requires installed authority" unless context
|
|
23
|
-
params = {"assignment_id" => require_option(options, :assignment, usage),
|
|
24
|
-
"attempt_id" => require_option(options, :attempt, usage), "head" => require_option(options, :head, usage),
|
|
25
|
-
"candidate_generation" => integer_option(options, :candidate_generation, usage, positive: true)}
|
|
26
|
-
raise Ace::Support::Cli::Error, "Campaign head must be an exact SHA-1" unless params["head"].match?(/\A[0-9a-f]{40}\z/)
|
|
27
|
-
context.verify_attempt_hints!(assignment_id: params.fetch("assignment_id"), attempt_id: params.fetch("attempt_id"))
|
|
28
|
-
path = require_option(options, :output, usage)
|
|
29
|
-
reply = context.client(options: options).call("campaign_export_result", params, download: true, purpose: :artifacts, timeout: 30)
|
|
30
|
-
bytes = reply.parts&.first
|
|
31
|
-
unless reply.parts&.size == 1 && bytes.is_a?(String) && bytes.bytesize <= 64 * 1024 &&
|
|
32
|
-
reply.data.values_at("head", "candidate_generation", "bytes", "sha256") ==
|
|
33
|
-
[params.fetch("head"), params.fetch("candidate_generation"), bytes.bytesize, Digest::SHA256.hexdigest(bytes)]
|
|
34
|
-
raise AttemptErrors::EvidenceUnavailable, "Campaign result download differs"
|
|
35
|
-
end
|
|
36
|
-
begin
|
|
37
|
-
File.open(path, File::WRONLY | File::CREAT | File::EXCL | File::NOFOLLOW, 0o600) do |file|
|
|
38
|
-
file.write(bytes)
|
|
39
|
-
file.flush
|
|
40
|
-
file.fsync
|
|
41
|
-
end
|
|
42
|
-
rescue Errno::EEXIST
|
|
43
|
-
raise Ace::Support::Cli::Error, "Existing campaign result file differs" unless input_bytes(path, limit: 64 * 1024) == bytes
|
|
44
|
-
end
|
|
45
|
-
emit_json(reply.data.except("transfer").merge("path" => path))
|
|
46
|
-
rescue SecurityError, Ace::Runtime::RuntimeUnavailableError
|
|
47
|
-
protected_transport_unavailable!
|
|
48
|
-
rescue SystemCallError, IOError
|
|
49
|
-
raise Ace::Support::Cli::Error, "Campaign result file or transport is unavailable"
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
end
|