ace-assign 0.70.0 → 0.73.0
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 +3 -2099
- 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 -1
- data/handbook/workflow-instructions/assign/drive.wf.md +47 -118
- 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 +2 -1
- 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 +4 -161
- 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/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/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_transfer.rb +0 -327
- 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/deployment.rb +0 -627
- data/lib/ace/assign/authority/deployment_history.rb +0 -146
- data/lib/ace/assign/authority/endcap.rb +0 -455
- 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 -207
- 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_settlement.rb +0 -152
- data/lib/ace/assign/authority/endcap_services.rb +0 -598
- 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/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 -94
- data/lib/ace/assign/authority/ordinary_effect_settlement.rb +0 -118
- data/lib/ace/assign/authority/ordinary_lifecycle.rb +0 -191
- 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_results.rb +0 -196
- data/lib/ace/assign/authority/ordinary_server.rb +0 -75
- 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 -304
- 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/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 -187
- 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 -153
- 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 -1912
- 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 -273
- 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 -314
- data/lib/ace/assign/molecules/terminal_scope_receipt.rb +0 -88
- data/lib/ace/assign/organisms/attempt_coordinator.rb +0 -1337
- data/lib/ace/assign/organisms/delivery_coordinator.rb +0 -342
- 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,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
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require_relative "protected_submission"
|
|
3
|
-
require_relative "../../authority/campaign_round_transfer"
|
|
4
|
-
|
|
5
|
-
module Ace
|
|
6
|
-
module Assign
|
|
7
|
-
module CLI
|
|
8
|
-
module Commands
|
|
9
|
-
class CampaignRecordRound < Ace::Support::Cli::Command
|
|
10
|
-
include Ace::Support::Cli::Base
|
|
11
|
-
include ProtectedSubmission
|
|
12
|
-
desc "Consume settled protected campaign children into the original R1 store"
|
|
13
|
-
option :mapping, desc: "Original installed parent mapping"
|
|
14
|
-
option :assignment, desc: "Original parent assignment"
|
|
15
|
-
option :attempt, desc: "Original parent attempt"
|
|
16
|
-
option :head, desc: "Exact parent candidate head"
|
|
17
|
-
option :candidate_generation, desc: "Exact parent candidate counter"
|
|
18
|
-
option :mutation, desc: "Stable R1 round attempt ID"
|
|
19
|
-
option :input, desc: "Closed bounded round envelope"
|
|
20
|
-
option :artifact, type: :array, desc: "Ordered report byte files"
|
|
21
|
-
|
|
22
|
-
def call(**options)
|
|
23
|
-
usage = "campaign-record-round with original parent selectors, --mutation ID and --input FILE"
|
|
24
|
-
context = protected_context(options)
|
|
25
|
-
raise AttemptErrors::EvidenceUnavailable, "Campaign consumption requires installed authority" unless context
|
|
26
|
-
params = {"assignment_id" => require_option(options, :assignment, usage),
|
|
27
|
-
"attempt_id" => require_option(options, :attempt, usage), "head" => require_option(options, :head, usage),
|
|
28
|
-
"candidate_generation" => integer_option(options, :candidate_generation, usage, positive: true)}
|
|
29
|
-
unless params["head"].match?(/\A[0-9a-f]{40}\z/)
|
|
30
|
-
raise Ace::Support::Cli::Error, "Campaign head must be an exact SHA-1"
|
|
31
|
-
end
|
|
32
|
-
context.verify_attempt_hints!(assignment_id: params.fetch("assignment_id"), attempt_id: params.fetch("attempt_id"))
|
|
33
|
-
mutation = require_option(options, :mutation, usage)
|
|
34
|
-
bytes = input_bytes(require_option(options, :input, usage), limit: 16 * 1024)
|
|
35
|
-
files = options.fetch(:artifact, [])
|
|
36
|
-
raise Ace::Support::Cli::Error, "Campaign artifacts require at most 16 files" unless files.is_a?(Array) && files.size <= 16
|
|
37
|
-
parts = [bytes, *files.map { |path| input_bytes(path, limit: 64 * 1024, nonempty: false) }]
|
|
38
|
-
params["input_sha256"] = Digest::SHA256.hexdigest(bytes)
|
|
39
|
-
input = Struct.new(:parts) do
|
|
40
|
-
def count = parts.length
|
|
41
|
-
def bytes(index:) = parts.fetch(index)
|
|
42
|
-
end.new(parts)
|
|
43
|
-
admitted = Ace::Assign::Authority::CampaignRoundTransfer.decode(input: input, sha256: params.fetch("input_sha256"))
|
|
44
|
-
unless admitted.fetch(:round).values_at("attempt_id", "head") == [mutation, params.fetch("head")]
|
|
45
|
-
raise Ace::Support::Cli::Error, "Campaign input request ID or head differs"
|
|
46
|
-
end
|
|
47
|
-
emit_json(upload_call(context.client(options: options), "campaign_record_round", params, mutation, parts, :receipt_artifacts))
|
|
48
|
-
rescue SecurityError, Ace::Runtime::RuntimeUnavailableError
|
|
49
|
-
protected_transport_unavailable!
|
|
50
|
-
rescue ArgumentError => error
|
|
51
|
-
raise Ace::Support::Cli::Error, error.message
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require "json"
|
|
3
|
-
require_relative "../../authority/protected_assignment_context"
|
|
4
|
-
require_relative "../../organisms/protected_delivery_coordinator"
|
|
5
|
-
|
|
6
|
-
module Ace
|
|
7
|
-
module Assign
|
|
8
|
-
module CLI
|
|
9
|
-
module Commands
|
|
10
|
-
class Delivery < Ace::Support::Cli::Command
|
|
11
|
-
desc "Execute or reconcile attempt-bound forge delivery"
|
|
12
|
-
option :assignment, required: true, desc: "Managed assignment ID"
|
|
13
|
-
option :attempt, required: true, desc: "Active delivery attempt ID"
|
|
14
|
-
option :operation, required: true, desc: "create, update, ready, review, merge, status"
|
|
15
|
-
option :parameters, desc: "JSON delivery parameters (otherwise assignment source delivery mapping)"
|
|
16
|
-
option :title, desc: "PR title"
|
|
17
|
-
option :body_file, desc: "PR description file"
|
|
18
|
-
option :tests, desc: "JSON reference to coordinator-accepted test attempt and receipt digest"
|
|
19
|
-
option :review, desc: "JSON reference to coordinator-accepted independent review attempt and receipt digest"
|
|
20
|
-
option :service_request, desc: "Original authorized PR service request ID"
|
|
21
|
-
option :mapping, desc: "Installed original protected mapping selector"
|
|
22
|
-
option :scope, desc: "Original prepared worker scope"
|
|
23
|
-
option :candidate_head, desc: "Exact original protected candidate SHA"
|
|
24
|
-
option :candidate_generation, type: :integer, desc: "Exact original protected candidate generation"
|
|
25
|
-
option :input_digest, desc: "Exact original accepted service input SHA256"
|
|
26
|
-
option :target, desc: "Exact original target repository or PR URL"
|
|
27
|
-
option :artifact_digest, desc: "Original target artifact SHA256, if present"
|
|
28
|
-
|
|
29
|
-
def initialize(protected_context: nil)
|
|
30
|
-
@protected_assignment_context = protected_context
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def call(**options)
|
|
34
|
-
context = @protected_assignment_context ||= Ace::Assign::Authority::ProtectedAssignmentContext.load
|
|
35
|
-
protected = context.protected_participant? || options[:mapping] || context.mapping_hint?
|
|
36
|
-
if protected
|
|
37
|
-
unless context.protected_worker?
|
|
38
|
-
raise AttemptErrors::EvidenceUnavailable, "protected delivery is worker-only"
|
|
39
|
-
end
|
|
40
|
-
unless %w[create update ready merge status].include?(options[:operation]) && %i[title body_file tests review].all? { |key| options[key].nil? }
|
|
41
|
-
raise ArgumentError, "protected delivery accepts canonical PR results/status only; local input/evidence flags are unavailable"
|
|
42
|
-
end
|
|
43
|
-
input = context.resolve(options: options, assignment_id: options[:assignment], scope: options[:scope])
|
|
44
|
-
raise AttemptErrors::EvidenceUnavailable, "original protected prepared input is unavailable" unless input
|
|
45
|
-
result = Organisms::ProtectedDeliveryCoordinator.new(client: context.client(options: options),
|
|
46
|
-
project_id: input.work.manifest.fetch("project_id")).perform(assignment_id: options[:assignment],
|
|
47
|
-
attempt_id: options[:attempt], operation: options[:operation], service_request_id: options[:service_request],
|
|
48
|
-
candidate_head: options[:candidate_head], candidate_generation: options[:candidate_generation],
|
|
49
|
-
input_digest: options[:input_digest], target: {"resource" => options[:target], "artifact_digest" => options[:artifact_digest]},
|
|
50
|
-
parameters: read_json(options[:parameters]))
|
|
51
|
-
puts JSON.generate(json_value(result))
|
|
52
|
-
return
|
|
53
|
-
end
|
|
54
|
-
if %i[mapping scope candidate_head candidate_generation input_digest target artifact_digest].any? { |key| options[key] }
|
|
55
|
-
raise ArgumentError, "protected delivery selectors cannot select ordinary local delivery"
|
|
56
|
-
end
|
|
57
|
-
result = build_delivery.perform(assignment_id: options[:assignment], attempt_id: options[:attempt],
|
|
58
|
-
operation: options[:operation], parameters: read_json(options[:parameters]),
|
|
59
|
-
title: options[:title], body: options[:body_file] && File.read(options[:body_file]),
|
|
60
|
-
tests: read_json(options[:tests]), review: read_json(options[:review]),
|
|
61
|
-
service_request_id: options[:service_request])
|
|
62
|
-
puts JSON.generate(json_value(result))
|
|
63
|
-
rescue ArgumentError, Ace::Git::Error, Ace::Assign::Error, Ace::Runtime::RuntimeUnavailableError,
|
|
64
|
-
JSON::ParserError, SecurityError, SystemCallError => e
|
|
65
|
-
message = e.is_a?(JSON::ParserError) ? "protected installed selection unavailable" : e.message
|
|
66
|
-
raise Ace::Support::Cli::Error, message
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
private
|
|
70
|
-
|
|
71
|
-
def build_delivery
|
|
72
|
-
Organisms::DeliveryCoordinator.new(repo_root: Ace::Support::Fs::Molecules::ProjectRootFinder.find_or_current)
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
def read_json(path)
|
|
76
|
-
return nil unless path
|
|
77
|
-
bytes = File.open(path, File::RDONLY | File::NOFOLLOW | File::NONBLOCK) do |file|
|
|
78
|
-
before = file.stat
|
|
79
|
-
raise ArgumentError, "Delivery input must be a bounded regular file" unless before.file? && before.size <= 65_536
|
|
80
|
-
value = file.read(65_537)
|
|
81
|
-
after = file.stat
|
|
82
|
-
unless value && value.bytesize <= 65_536 &&
|
|
83
|
-
[before.dev, before.ino, before.size, before.mtime, before.ctime] == [after.dev, after.ino, after.size, after.mtime, after.ctime]
|
|
84
|
-
raise ArgumentError, "Delivery input changed or exceeds its bound"
|
|
85
|
-
end
|
|
86
|
-
value
|
|
87
|
-
end
|
|
88
|
-
data = JSON.parse(bytes, max_nesting: 16, create_additions: false,
|
|
89
|
-
allow_duplicate_key: false, allow_comments: false, allow_nan: false)
|
|
90
|
-
raise ArgumentError, "Delivery input must be a JSON object" unless data.is_a?(Hash)
|
|
91
|
-
data
|
|
92
|
-
rescue JSON::ParserError
|
|
93
|
-
raise ArgumentError, "Delivery input must be valid JSON"
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
def json_value(value)
|
|
97
|
-
case value
|
|
98
|
-
when Hash then value.transform_values { |item| json_value(item) }
|
|
99
|
-
when Array then value.map { |item| json_value(item) }
|
|
100
|
-
else value.respond_to?(:to_h) ? json_value(value.to_h) : value
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
end
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require_relative "attempt/base"
|
|
3
|
-
|
|
4
|
-
module Ace
|
|
5
|
-
module Assign
|
|
6
|
-
module CLI
|
|
7
|
-
module Commands
|
|
8
|
-
class InboxBind < Ace::Support::Cli::Command
|
|
9
|
-
include Ace::Support::Cli::Base
|
|
10
|
-
include Attempt::Base
|
|
11
|
-
|
|
12
|
-
desc "Bind an original protected Inbox registration before scope closure"
|
|
13
|
-
option :mapping, desc: "Exact installed mapping ID"
|
|
14
|
-
option :assignment, desc: "Exact original assignment ID"
|
|
15
|
-
option :attempt, desc: "Exact original attempt ID"
|
|
16
|
-
option :event, desc: "Original Inbox event ID"
|
|
17
|
-
option :inbox_context, desc: "Installed Inbox context ID"
|
|
18
|
-
option :mutation, desc: "Stable original authority mutation ID"
|
|
19
|
-
option :expected_generation, desc: "Original authority mutation generation"
|
|
20
|
-
|
|
21
|
-
def call(**options)
|
|
22
|
-
context = protected_context(options)
|
|
23
|
-
raise AttemptErrors::EvidenceUnavailable, "Inbox binding requires installed protected authority" unless context
|
|
24
|
-
usage = "inbox-bind --mapping MAP --assignment ID --attempt ID --event ID --inbox-context ID --mutation ID --expected-generation N"
|
|
25
|
-
client, params, mutation = protected_attempt_request(context, options, usage)
|
|
26
|
-
params.merge!("event_id" => require_option(options, :event, usage),
|
|
27
|
-
"inbox_context_id" => require_option(options, :inbox_context, usage))
|
|
28
|
-
emit_json(protected_call(client, "bind_inbox", params, mutation))
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require_relative "attempt/base"
|
|
3
|
-
require "digest"
|
|
4
|
-
|
|
5
|
-
module Ace
|
|
6
|
-
module Assign
|
|
7
|
-
module CLI
|
|
8
|
-
module Commands
|
|
9
|
-
# Local files provide bounded bytes; only the existing authority grants admission.
|
|
10
|
-
module ProtectedSubmission
|
|
11
|
-
include Attempt::Base
|
|
12
|
-
private
|
|
13
|
-
|
|
14
|
-
def require_option(options, key, usage)
|
|
15
|
-
value = options[key].to_s.strip
|
|
16
|
-
raise Ace::Support::Cli::Error, "Missing --#{key}: usage: ace-assign #{usage}" if value.empty?
|
|
17
|
-
value
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def submission_request(options, usage)
|
|
21
|
-
context = protected_context(options)
|
|
22
|
-
raise AttemptErrors::EvidenceUnavailable, "Submission requires installed protected authority" unless context
|
|
23
|
-
protected_attempt_request(context, options, usage)
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def input_bytes(path, limit:, nonempty: true)
|
|
27
|
-
File.open(path, File::RDONLY | File::NOFOLLOW | File::NONBLOCK) do |file|
|
|
28
|
-
before = file.stat
|
|
29
|
-
raise Ace::Support::Cli::Error, "Input must be a bounded regular file" unless before.file? && before.size.between?(nonempty ? 1 : 0, limit)
|
|
30
|
-
bytes = file.read(limit + 1)
|
|
31
|
-
after = file.stat
|
|
32
|
-
selected = File.lstat(path)
|
|
33
|
-
identity = ->(stat) { [stat.dev, stat.ino, stat.size, stat.mtime, stat.ctime] }
|
|
34
|
-
unless bytes.bytesize == before.size && identity.call(before) == identity.call(after) &&
|
|
35
|
-
identity.call(after) == identity.call(selected) && !selected.symlink?
|
|
36
|
-
raise Ace::Support::Cli::Error, "Input changed during bounded read"
|
|
37
|
-
end
|
|
38
|
-
bytes.freeze
|
|
39
|
-
end
|
|
40
|
-
rescue SystemCallError, IOError
|
|
41
|
-
raise Ace::Support::Cli::Error, "Input file is unavailable"
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def input_json(bytes)
|
|
45
|
-
text = bytes.dup.force_encoding(Encoding::UTF_8)
|
|
46
|
-
raise Ace::Support::Cli::Error, "Input JSON is malformed" unless text.valid_encoding?
|
|
47
|
-
JSON.parse(text, create_additions: false, max_nesting: 32, allow_duplicate_key: false, allow_comments: false)
|
|
48
|
-
rescue JSON::ParserError
|
|
49
|
-
raise Ace::Support::Cli::Error, "Input JSON is malformed"
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def upload_call(client, operation, params, mutation, parts, purpose)
|
|
53
|
-
client.call(operation, params, mutation_id: mutation, upload_parts: parts, purpose: purpose, timeout: 30).data
|
|
54
|
-
rescue SecurityError, Ace::Runtime::RuntimeUnavailableError, SystemCallError, IOError
|
|
55
|
-
protected_transport_unavailable!
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "json"
|
|
4
|
-
|
|
5
|
-
module Ace
|
|
6
|
-
module Assign
|
|
7
|
-
module CLI
|
|
8
|
-
module Commands
|
|
9
|
-
class Resume < Ace::Support::Cli::Command
|
|
10
|
-
include Ace::Support::Cli::Base
|
|
11
|
-
include AssignmentTarget
|
|
12
|
-
desc "Recover accepted assignment state without launching another writer"
|
|
13
|
-
option :assignment, desc: "Assignment ID"
|
|
14
|
-
option :mapping, desc: "Installed protected launch mapping ID"
|
|
15
|
-
option :attempt, desc: "Original protected attempt ID"
|
|
16
|
-
option :dry_run, type: :boolean, default: false, desc: "Inspect recovery without writing observations"
|
|
17
|
-
|
|
18
|
-
def call(**options)
|
|
19
|
-
target = resolve_assignment_target(options)
|
|
20
|
-
if target.prepared_input
|
|
21
|
-
raise Ace::Support::Cli::Error, "protected resume only supports --dry-run" unless options[:dry_run]
|
|
22
|
-
result = build_executor_for_target(target).status
|
|
23
|
-
descriptor = target.prepared_input.descriptor
|
|
24
|
-
puts JSON.generate(descriptor.slice("assignment_id", "mapping_id", "attempt_id", "scope", "definition_digest", "selection_sha256").merge(
|
|
25
|
-
"state" => result.fetch(:state).assignment_state.to_s, "dry_run" => true))
|
|
26
|
-
return
|
|
27
|
-
end
|
|
28
|
-
id = options[:assignment].to_s.strip
|
|
29
|
-
raise Ace::Support::Cli::Error, "--assignment ID is required" if id.empty?
|
|
30
|
-
|
|
31
|
-
puts JSON.generate(Organisms::AttemptCoordinator.new.resume(
|
|
32
|
-
assignment_id: id, dry_run: options[:dry_run]))
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
end
|