hecks 3.12.0 → 3.13.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/lib/hecks/adapters/driven/script_runner.adapter +3 -0
- data/lib/hecks/adapters/driven/script_runner.rb +93 -0
- data/lib/hecks/adapters/driven.rb +1 -0
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +16 -12
- data/lib/hecks/bluebook/meta_validator.rb +28 -21
- data/lib/hecks/deploy/bluebook/deploy.bluebook +1 -1
- data/lib/hecks/framework/bluebook/change.behaviors +30 -0
- data/lib/hecks/framework/bluebook/change.bluebook +162 -0
- data/lib/hecks/framework/bluebook/change.hecksagon +21 -0
- data/lib/hecks/framework/change.oidc.json +34 -0
- data/lib/hecks/framework/console_settings.oidc.json +103 -0
- data/lib/hecks/framework/governance.oidc.json +28 -0
- data/lib/hecks/framework/identity.oidc.json +18 -0
- data/lib/hecks/framework/privacy.oidc.json +30 -0
- data/lib/hecks/gate/stages.yml +3 -0
- data/lib/hecks/grammar/translation.oidc.json +48 -0
- data/lib/hecks/hecks/adapters/codebase/test_runner.rb +12 -1
- data/lib/hecks/hecks/adapters/deploy_toolchain.rb +2 -1
- data/lib/hecks/projections/deploy/fargate/cdn/lines.rb +12 -1
- data/lib/hecks/projections/site/admin_cms/templates/membership.rb +10 -7
- data/lib/hecks/projections/site/edge/behaviours.rb +10 -6
- data/lib/hecks/projections/site/edge/checks.rb +2 -0
- data/lib/hecks/projections/site/edge/function_checks.rb +104 -0
- data/lib/hecks/projections/site/edge/function_code.rb +86 -0
- data/lib/hecks/projections/site/edge/redirect_host.js.tmpl +65 -0
- data/lib/hecks/projections/site/edge.rb +35 -9
- data/lib/hecks/projections/site/site_cdn.rb +21 -7
- data/lib/hecks/projections/site/table/row_builder.rb +12 -2
- data/lib/hecks/projections/site/table.rb +4 -2
- data/lib/hecks/site/bluebook/site.bluebook +16 -1
- data/lib/hecks/tools/oidc_manifests.rb +44 -6
- data/lib/hecks/tools/site_routes/templates.rb +13 -9
- data/lib/hecks/version.rb +1 -1
- data/prebuilt/{verdicts-df19f35868d3284a70f55e42dcef87ef4e65e6a678e5135f72e1f6cff4c1357b.json → verdicts-48318ca172edd21443b1ad02801c697fff100b746e828f09db987647641a5df2.json} +1 -1
- data/rust/host/HECKS_RELEASE +1 -1
- data/rust/host/src/auth.rs +58 -0
- data/rust/host/src/platform_signin/session.rs +1 -1
- metadata +21 -7
- /data/lib/hecks/framework/{oidc.json → compliance.oidc.json} +0 -0
- /data/lib/hecks/grammar/{oidc.json → expression.oidc.json} +0 -0
- /data/prebuilt/{d74a00e996222c1d8b676d5959092effc46b4dc59e0555f3bf9554ac58e3094a.marshal → 2b59737fbdbd5f44edda0fddce2611f45d76087b3a7c782d1f1a17ea58c77948.marshal} +0 -0
- /data/prebuilt/{9ee54e2f0d4d6d3fe046827779b221e02de665fba911a048ae5dd8b8fbfb7d90.marshal → 58208399bada10b0db99928c48e38789288fc58f7b71a72b21bfa386680e32ed.marshal} +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e1a1eacc3bae3f940928e23269c95e0306f291ec5a2b6d62268e254f12f1902e
|
|
4
|
+
data.tar.gz: e4168d9e617dbad8912a65dbe10be8a3a32a6a08b18fd84542a05fa9803aeaef
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a49c8992b247138d92232d00123dd73986be81040bb1356fc912f97905644bbb35a38af431a1d81475b9ce07c644ab8c2aab3370c3fabaf685c0f8a93a16bd70
|
|
7
|
+
data.tar.gz: 48ccea615a818989da2a01babb5c9d344a5a7c94fbe2eea47301a1437f29aa671134011a626f317c128bcaf1aaa912a21f056e30c8b133643123df129aa7a470
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
require "digest"
|
|
2
|
+
require "json"
|
|
3
|
+
|
|
4
|
+
module Hecks
|
|
5
|
+
module Adapters
|
|
6
|
+
# The `Script` port's adapter for a `Change`: it runs the scripts registered with it, in this
|
|
7
|
+
# process. A script is a name, a `plan` that says what it would do without doing it, and an
|
|
8
|
+
# `apply` that does it.
|
|
9
|
+
#
|
|
10
|
+
# `examine` plans a change and answers the plan with a digest of it. `perform` plans again and
|
|
11
|
+
# carries the change out only when the plan still has the digest that was confirmed, so what
|
|
12
|
+
# runs is what the person saw. A script that cannot be found, or whose plan or apply raises,
|
|
13
|
+
# refuses the change with its message.
|
|
14
|
+
class ScriptRunner
|
|
15
|
+
# A script: what it would do, and doing it. Both take the arguments as a Hash of String keys.
|
|
16
|
+
Script = Struct.new(:plan, :apply, keyword_init: true)
|
|
17
|
+
|
|
18
|
+
# Why a change was refused: an unknown script, a plan that moved, or the script's own failure.
|
|
19
|
+
class Refused < StandardError; end
|
|
20
|
+
|
|
21
|
+
class << self
|
|
22
|
+
# @return [Hash{String => Script}] the scripts this process can run, by name
|
|
23
|
+
def scripts = @scripts ||= {}
|
|
24
|
+
|
|
25
|
+
# @param name [String] the name a change asks for
|
|
26
|
+
# @param plan [#call] takes the arguments, returns the text of what the script would do
|
|
27
|
+
# @param apply [#call] takes the arguments, does it, returns the text of what happened
|
|
28
|
+
# @return [Script] the registered script
|
|
29
|
+
def register(name, plan:, apply:) = scripts[name.to_s] = Script.new(plan: plan, apply: apply)
|
|
30
|
+
|
|
31
|
+
# Forgets every registered script.
|
|
32
|
+
#
|
|
33
|
+
# @return [Hash] the empty registry
|
|
34
|
+
def reset! = @scripts = {}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Accepts the arguments every driven adapter is built with and keeps none of them.
|
|
38
|
+
#
|
|
39
|
+
# @param aggregate [Object, nil] unused
|
|
40
|
+
# @param settings [Hash] unused
|
|
41
|
+
# @param root [String, nil] unused
|
|
42
|
+
def initialize(aggregate: nil, settings: {}, root: nil); end
|
|
43
|
+
|
|
44
|
+
# Plans the change.
|
|
45
|
+
#
|
|
46
|
+
# @param held [Hash] the record: `script` and `arguments`
|
|
47
|
+
# @return [Hash{Symbol => Hash}] `plan:` what the script would do, and `digest:` its
|
|
48
|
+
# fingerprint
|
|
49
|
+
# @raise [Refused] when the script is unknown or its plan raises
|
|
50
|
+
def examine(**held)
|
|
51
|
+
name, args = request(held)
|
|
52
|
+
text = planned(name, args)
|
|
53
|
+
{ plan: { value: text }, digest: { value: digest(name, args, text) } }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Carries out a confirmed change if its plan has not moved.
|
|
57
|
+
#
|
|
58
|
+
# @param held [Hash] the record: `script`, `arguments` and `planned_digest`
|
|
59
|
+
# @return [Hash{Symbol => Hash}] `report:` what the script reported
|
|
60
|
+
# @raise [Refused] when the script is unknown, the plan has moved, or the script raises
|
|
61
|
+
def perform(**held)
|
|
62
|
+
name, args = request(held)
|
|
63
|
+
current = digest(name, args, planned(name, args))
|
|
64
|
+
raise Refused, "the plan moved since it was confirmed; plan it again" unless current == plain(held[:planned_digest])
|
|
65
|
+
|
|
66
|
+
{ report: { value: run(name, :apply, args).to_s } }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def request(held) = [plain(held[:script]), arguments(held[:arguments])]
|
|
72
|
+
|
|
73
|
+
def planned(name, args) = run(name, :plan, args).to_s
|
|
74
|
+
|
|
75
|
+
def run(name, step, args)
|
|
76
|
+
script = self.class.scripts.fetch(name) { raise Refused, "no script is registered as #{name.inspect}" }
|
|
77
|
+
script.public_send(step).call(args)
|
|
78
|
+
rescue Refused
|
|
79
|
+
raise
|
|
80
|
+
rescue StandardError => e
|
|
81
|
+
raise Refused, "#{name} #{step} failed: #{e.message}"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# The digest stands for the script, its arguments and its plan, so none of them can change
|
|
85
|
+
# between confirming and applying.
|
|
86
|
+
def digest(name, args, text) = Digest::SHA256.hexdigest(JSON.generate([name, args.sort.to_h, text]))
|
|
87
|
+
|
|
88
|
+
def arguments(list) = Array(list).to_h { |pair| [plain(pair[:key]), plain(pair[:value])] }
|
|
89
|
+
|
|
90
|
+
def plain(cell) = cell.is_a?(Hash) && cell.key?(:value) ? cell[:value] : cell
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -37,19 +37,23 @@ module Hecks
|
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
# Dispatches every seed row into a fresh "Bluebook" instance and
|
|
40
|
-
# reads the result back.
|
|
40
|
+
# reads the result back. The table is memoized for the process, so it is built with no
|
|
41
|
+
# ambient caller: an enclosing `Hecks.as_caller` would have the language's own
|
|
42
|
+
# `Language`-role commands refused.
|
|
41
43
|
def boot
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
44
|
+
Runtime::Caller.without do
|
|
45
|
+
bluebook = MetaValidator.grammar_registry.bluebook("Bluebook")
|
|
46
|
+
# `fresh_runtime`, not a new `Runtime::Registry` — the grammar
|
|
47
|
+
# registry already has "Bluebook" registered and its adapter
|
|
48
|
+
# ports already loaded.
|
|
49
|
+
runtime = MetaValidator.fresh_runtime
|
|
50
|
+
|
|
51
|
+
declare_syntax(runtime, bluebook)
|
|
52
|
+
admit_keywords(runtime, bluebook)
|
|
53
|
+
admit_arguments(runtime, bluebook)
|
|
54
|
+
|
|
55
|
+
read_back(runtime, bluebook)
|
|
56
|
+
end
|
|
53
57
|
end
|
|
54
58
|
|
|
55
59
|
# An Integer stays an Integer — `position` is `Position`-typed, so
|
|
@@ -168,32 +168,39 @@ module Hecks
|
|
|
168
168
|
end
|
|
169
169
|
|
|
170
170
|
# The booted, self-judged language registry every ordinary bluebook is
|
|
171
|
-
# judged against — built once per process and memoized, with shadow parsing off
|
|
172
|
-
# inside `while_shadow_parsing` that is first to ask would
|
|
173
|
-
# parsed in that mode, or fail to load it
|
|
171
|
+
# judged against — built once per process and memoized, with shadow parsing off and no
|
|
172
|
+
# ambient caller bound. A caller inside `while_shadow_parsing` that is first to ask would
|
|
173
|
+
# otherwise memoize a grammar parsed in that mode, or fail to load it; one inside
|
|
174
|
+
# `Hecks.as_caller` would have the language's own `Language`-role commands refused, and the
|
|
175
|
+
# grammar would be memoized without the chapters those refusals dropped.
|
|
174
176
|
#
|
|
175
177
|
# @return [Runtime::Registry] the booted, fixpoint-judged language
|
|
176
178
|
# registry
|
|
177
179
|
def self.grammar_registry
|
|
178
|
-
@grammar_registry ||= while_not_shadow_parsing
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
registry
|
|
180
|
+
@grammar_registry ||= while_not_shadow_parsing { Runtime::Caller.without { build_grammar_registry } }
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# The one-time build behind `grammar_registry`.
|
|
184
|
+
#
|
|
185
|
+
# @return [Runtime::Registry] the booted, fixpoint-judged language registry
|
|
186
|
+
def self.build_grammar_registry
|
|
187
|
+
registry = load_grammar_into(Runtime::Registry.new)
|
|
188
|
+
# Assigned before the fixpoint judge below, since judging re-enters
|
|
189
|
+
# grammar_registry (via fresh_runtime/Plan.for) and a bare `||=`
|
|
190
|
+
# would still be nil mid-evaluation, recursing forever.
|
|
191
|
+
@grammar_registry = registry
|
|
192
|
+
# The language now judges itself; every bluebook from here on is
|
|
193
|
+
# judged by that assembled graph, not the raw bootstrap one. Must
|
|
194
|
+
# run after load_grammar_into's ensure clears @bootstrapping.
|
|
195
|
+
while_forcing_fixpoint do
|
|
196
|
+
LANGUAGE_CHAPTERS.each { |name| registry.add_bluebook(call(registry.bluebook(name))) }
|
|
197
|
+
load_attached_grammar_into(registry)
|
|
196
198
|
end
|
|
199
|
+
# Keyed on this registry's object identity, not a bare boolean, so
|
|
200
|
+
# a manual reset can never leave a stale "ready" from the previous
|
|
201
|
+
# cycle.
|
|
202
|
+
@grammar_ready_for = registry.object_id
|
|
203
|
+
registry
|
|
197
204
|
end
|
|
198
205
|
|
|
199
206
|
# Whether `grammar_registry`'s memoized singleton has finished its
|
|
@@ -2806,7 +2806,7 @@ Hecks.bluebook "Deploy" do
|
|
|
2806
2806
|
end
|
|
2807
2807
|
|
|
2808
2808
|
command "ProjectOidc" do
|
|
2809
|
-
goal "Write each domain's OIDC client and scope manifest beside it
|
|
2809
|
+
goal "Write each domain's OIDC client and scope manifest beside it: oidc.json, or one <chapter>.oidc.json for each chapter of a directory that holds several"
|
|
2810
2810
|
|
|
2811
2811
|
attribute :domains, DomainList, optional: true
|
|
2812
2812
|
attribute :run, RunKey
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
Hecks.behaviors "Change" do
|
|
2
|
+
vision "A change names its script, faults when that script cannot plan it, and is confirmed only from a plan."
|
|
3
|
+
|
|
4
|
+
loads "change.bluebook", "governance.bluebook", "change.hecksagon", "../../../../spec/fixtures/change/governance.hecksagon"
|
|
5
|
+
|
|
6
|
+
test "Request refuses a change that names no script" do
|
|
7
|
+
tests "Request", on: "Change"
|
|
8
|
+
input run: { value: "c1" }, script: { value: "" }
|
|
9
|
+
expect refused: "must match"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
test "Request refuses a change that is not named" do
|
|
13
|
+
tests "Request", on: "Change"
|
|
14
|
+
input run: { value: "" }, script: { value: "rename" }
|
|
15
|
+
expect refused: "must match"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
test "Request faults a change whose script nobody registered" do
|
|
19
|
+
tests "Request", on: "Change"
|
|
20
|
+
input run: { value: "c2" }, script: { value: "no_such_script" }
|
|
21
|
+
expect status: "faulted"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
test "Confirm refuses a change that was never planned" do
|
|
25
|
+
tests "Confirm", on: "Change"
|
|
26
|
+
setup "Change::Change.Request", run: { value: "c3" }, script: { value: "no_such_script" }
|
|
27
|
+
input run: { value: "c3" }, digest: { value: "ab12" }
|
|
28
|
+
expect refused: "the digest confirmed is the digest planned"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
Hecks.bluebook "Change" do
|
|
2
|
+
vision "A change you can preview: it is planned, shown, confirmed against exactly that plan, and only then carried out, whatever script it runs."
|
|
3
|
+
supporting
|
|
4
|
+
|
|
5
|
+
# A framework member like Governance and Identity: attach it with `attaches "Change"` in the
|
|
6
|
+
# consuming domain's own `.hecksagon`. The script is the port's adapter, so what a change
|
|
7
|
+
# does lives behind the `Script` port, and this chapter holds only how any change moves.
|
|
8
|
+
aggregate "Change" do
|
|
9
|
+
description "One change a script makes, previewed before it is carried out and applied only if its plan is unchanged."
|
|
10
|
+
|
|
11
|
+
attribute :run, RunKey
|
|
12
|
+
attribute :script, ScriptName
|
|
13
|
+
attribute :arguments, list_of(Argument), optional: true
|
|
14
|
+
attribute :plan, Report, optional: true
|
|
15
|
+
attribute :planned_digest, Digest, optional: true
|
|
16
|
+
attribute :report, Report, optional: true
|
|
17
|
+
attribute :refusal, Report, optional: true
|
|
18
|
+
identified_by :run
|
|
19
|
+
|
|
20
|
+
value_object "RunKey" do
|
|
21
|
+
attribute :value, String, pattern: '[^ \t\n\r]'
|
|
22
|
+
|
|
23
|
+
invariant("a change is named") { !value.to_s.empty? }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# The name the script is registered under with the adapter.
|
|
27
|
+
value_object "ScriptName" do
|
|
28
|
+
attribute :value, String, pattern: '[^ \t\n\r]'
|
|
29
|
+
|
|
30
|
+
invariant("a change names its script") { !value.to_s.empty? }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# One argument of the script, spelled as text; the script says what each key means.
|
|
34
|
+
value_object "Argument" do
|
|
35
|
+
attribute :key, String
|
|
36
|
+
attribute :value, String
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
value_object "Report" do
|
|
40
|
+
attribute :value, String
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# A fingerprint of the plan, the script and its arguments: confirming one digest applies
|
|
44
|
+
# that plan and no other.
|
|
45
|
+
value_object "Digest" do
|
|
46
|
+
attribute :value, String, pattern: '[0-9a-f]+'
|
|
47
|
+
|
|
48
|
+
invariant("a plan has a digest") { !value.to_s.empty? }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
lifecycle :status, default: "requested" do
|
|
52
|
+
mark :failure, "faulted"
|
|
53
|
+
transition "Plan" => "planned", from: "requested"
|
|
54
|
+
transition "Confirm" => "confirmed", from: "planned"
|
|
55
|
+
transition "Complete" => "applied", from: "confirmed"
|
|
56
|
+
transition "Fault" => "faulted", from: ["requested", "planned", "confirmed"]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
command "Request" do
|
|
60
|
+
role "Maintainer"
|
|
61
|
+
goal "Ask for a change by naming its script and giving the script's arguments"
|
|
62
|
+
|
|
63
|
+
attribute :run, RunKey
|
|
64
|
+
attribute :script, ScriptName
|
|
65
|
+
attribute :arguments, list_of(Argument), optional: true
|
|
66
|
+
|
|
67
|
+
sets :run
|
|
68
|
+
sets :script
|
|
69
|
+
sets :arguments
|
|
70
|
+
|
|
71
|
+
emits ChangeRequested
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
command "Plan" do
|
|
75
|
+
role "System"
|
|
76
|
+
goal "Record what the script would do, and the digest that stands for it"
|
|
77
|
+
|
|
78
|
+
reference_to Change
|
|
79
|
+
attribute :plan, Report
|
|
80
|
+
attribute :digest, Digest
|
|
81
|
+
|
|
82
|
+
sets :plan
|
|
83
|
+
sets :planned_digest, to: :digest
|
|
84
|
+
|
|
85
|
+
emits ChangePlanned
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
command "Confirm" do
|
|
89
|
+
role "Maintainer"
|
|
90
|
+
goal "Agree to the plan shown, by naming its digest"
|
|
91
|
+
|
|
92
|
+
reference_to Change
|
|
93
|
+
attribute :digest, Digest
|
|
94
|
+
|
|
95
|
+
given("the digest confirmed is the digest planned") { digest.value == planned_digest.value }
|
|
96
|
+
|
|
97
|
+
emits ChangeConfirmed
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
command "Complete" do
|
|
101
|
+
role "System"
|
|
102
|
+
goal "Record that the confirmed plan was carried out, and what the script reported"
|
|
103
|
+
|
|
104
|
+
reference_to Change
|
|
105
|
+
attribute :report, Report
|
|
106
|
+
|
|
107
|
+
sets :report
|
|
108
|
+
|
|
109
|
+
emits ChangeApplied
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
command "Fault" do
|
|
113
|
+
role "System"
|
|
114
|
+
goal "Record that the change was refused or could not be carried out, and why"
|
|
115
|
+
|
|
116
|
+
reference_to Change
|
|
117
|
+
attribute :refusal, Report
|
|
118
|
+
|
|
119
|
+
sets :refusal
|
|
120
|
+
|
|
121
|
+
emits ChangeFaulted
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
query "Outcome" do
|
|
125
|
+
description "How one change stands: its status, its plan, and what it reported or why it failed."
|
|
126
|
+
attribute :run, RunKey
|
|
127
|
+
where(run: :run)
|
|
128
|
+
order_by :run
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
query "Faulted" do
|
|
132
|
+
description "Every change that was refused or could not be carried out."
|
|
133
|
+
where(status: "faulted")
|
|
134
|
+
order_by :run
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
policy "ExamineWhenRequested" do
|
|
139
|
+
on "Change.ChangeRequested"
|
|
140
|
+
trigger Change::Script::Examine, with: { run: :run }
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
policy "RecordThePlanWhenExamined" do
|
|
144
|
+
on "Change.ChangeExamined"
|
|
145
|
+
trigger Change::Plan, with: { plan: :plan, digest: :digest }
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
policy "PerformWhenConfirmed" do
|
|
149
|
+
on "Change.ChangeConfirmed"
|
|
150
|
+
trigger Change::Script::Perform, with: { run: :run }
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
policy "RecordTheOutcomeWhenPerformed" do
|
|
154
|
+
on "Change.ChangeAnswered"
|
|
155
|
+
trigger Change::Complete, with: { report: :report }
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
policy "RecordTheRefusal" do
|
|
159
|
+
on "Change.ChangeRefused"
|
|
160
|
+
trigger Change::Fault, with: { refusal: :refusal }
|
|
161
|
+
end
|
|
162
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Hecks.hecksagon "Change" do
|
|
2
|
+
# `Change.Request` and `Change.Confirm` declare roles; same reason as Compliance.
|
|
3
|
+
attaches "Governance"
|
|
4
|
+
persisted_by "Memory"
|
|
5
|
+
|
|
6
|
+
# The change moves through two asks: `Examine` plans it, `Perform` carries it out. What a
|
|
7
|
+
# script does is the adapter's, so the chapter never names one.
|
|
8
|
+
Change::Change.port "Script" do
|
|
9
|
+
asks "Examine", to: Change do
|
|
10
|
+
attribute :run, RunKey
|
|
11
|
+
answers "ChangeExamined"
|
|
12
|
+
refuses "ChangeRefused"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
asks "Perform", to: Change do
|
|
16
|
+
attribute :run, RunKey
|
|
17
|
+
answers "ChangeAnswered"
|
|
18
|
+
refuses "ChangeRefused"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"audience": "Change",
|
|
3
|
+
"scopes": [
|
|
4
|
+
{
|
|
5
|
+
"scope": "change:change.complete",
|
|
6
|
+
"verb": "Change::Change.Complete",
|
|
7
|
+
"role": "System"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"scope": "change:change.confirm",
|
|
11
|
+
"verb": "Change::Change.Confirm",
|
|
12
|
+
"role": "Maintainer"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"scope": "change:change.fault",
|
|
16
|
+
"verb": "Change::Change.Fault",
|
|
17
|
+
"role": "System"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"scope": "change:change.plan",
|
|
21
|
+
"verb": "Change::Change.Plan",
|
|
22
|
+
"role": "System"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"scope": "change:change.request",
|
|
26
|
+
"verb": "Change::Change.Request",
|
|
27
|
+
"role": "Maintainer"
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"roles": [
|
|
31
|
+
"Maintainer",
|
|
32
|
+
"System"
|
|
33
|
+
]
|
|
34
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
{
|
|
2
|
+
"audience": "ConsoleSettings",
|
|
3
|
+
"scopes": [
|
|
4
|
+
{
|
|
5
|
+
"scope": "console_settings:collection.declare",
|
|
6
|
+
"verb": "ConsoleSettings::Collection.Declare",
|
|
7
|
+
"role": "Console"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"scope": "console_settings:collection.replace_columns",
|
|
11
|
+
"verb": "ConsoleSettings::Collection.ReplaceColumns",
|
|
12
|
+
"role": "Console"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"scope": "console_settings:collection.replace_detail_field_columns",
|
|
16
|
+
"verb": "ConsoleSettings::Collection.ReplaceDetailFieldColumns",
|
|
17
|
+
"role": "Console"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"scope": "console_settings:collection.replace_detail_fields",
|
|
21
|
+
"verb": "ConsoleSettings::Collection.ReplaceDetailFields",
|
|
22
|
+
"role": "Console"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"scope": "console_settings:collection.replace_field_formats",
|
|
26
|
+
"verb": "ConsoleSettings::Collection.ReplaceFieldFormats",
|
|
27
|
+
"role": "Console"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"scope": "console_settings:collection.replace_preconditions",
|
|
31
|
+
"verb": "ConsoleSettings::Collection.ReplacePreconditions",
|
|
32
|
+
"role": "Console"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"scope": "console_settings:collection.set_extra",
|
|
36
|
+
"verb": "ConsoleSettings::Collection.SetExtra",
|
|
37
|
+
"role": "Console"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"scope": "console_settings:collection.set_identity",
|
|
41
|
+
"verb": "ConsoleSettings::Collection.SetIdentity",
|
|
42
|
+
"role": "Console"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"scope": "console_settings:collection.set_key",
|
|
46
|
+
"verb": "ConsoleSettings::Collection.SetKey",
|
|
47
|
+
"role": "Console"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"scope": "console_settings:collection.set_label",
|
|
51
|
+
"verb": "ConsoleSettings::Collection.SetLabel",
|
|
52
|
+
"role": "Console"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"scope": "console_settings:collection.set_list_query",
|
|
56
|
+
"verb": "ConsoleSettings::Collection.SetListQuery",
|
|
57
|
+
"role": "Console"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"scope": "console_settings:collection.set_nav_order",
|
|
61
|
+
"verb": "ConsoleSettings::Collection.SetNavOrder",
|
|
62
|
+
"role": "Console"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"scope": "console_settings:collection.set_primary_field",
|
|
66
|
+
"verb": "ConsoleSettings::Collection.SetPrimaryField",
|
|
67
|
+
"role": "Console"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"scope": "console_settings:overview.declare",
|
|
71
|
+
"verb": "ConsoleSettings::Overview.Declare",
|
|
72
|
+
"role": "Console"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"scope": "console_settings:overview.replace_stats",
|
|
76
|
+
"verb": "ConsoleSettings::Overview.ReplaceStats",
|
|
77
|
+
"role": "Console"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"scope": "console_settings:state_style.declare",
|
|
81
|
+
"verb": "ConsoleSettings::StateStyle.Declare",
|
|
82
|
+
"role": "Console"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"scope": "console_settings:state_style.set_attention",
|
|
86
|
+
"verb": "ConsoleSettings::StateStyle.SetAttention",
|
|
87
|
+
"role": "Console"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"scope": "console_settings:state_style.set_extra",
|
|
91
|
+
"verb": "ConsoleSettings::StateStyle.SetExtra",
|
|
92
|
+
"role": "Console"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"scope": "console_settings:state_style.set_tone",
|
|
96
|
+
"verb": "ConsoleSettings::StateStyle.SetTone",
|
|
97
|
+
"role": "Console"
|
|
98
|
+
}
|
|
99
|
+
],
|
|
100
|
+
"roles": [
|
|
101
|
+
"Console"
|
|
102
|
+
]
|
|
103
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"audience": "Governance",
|
|
3
|
+
"scopes": [
|
|
4
|
+
{
|
|
5
|
+
"scope": "governance:role_assignment.assign",
|
|
6
|
+
"verb": "Governance::RoleAssignment.Assign",
|
|
7
|
+
"role": "Governance administrator"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"scope": "governance:role_assignment.revoke",
|
|
11
|
+
"verb": "Governance::RoleAssignment.Revoke",
|
|
12
|
+
"role": "Governance administrator"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"scope": "governance:role_transition.grant",
|
|
16
|
+
"verb": "Governance::RoleTransition.Grant",
|
|
17
|
+
"role": "Governance administrator"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"scope": "governance:role_transition.revoke",
|
|
21
|
+
"verb": "Governance::RoleTransition.Revoke",
|
|
22
|
+
"role": "Governance administrator"
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"roles": [
|
|
26
|
+
"Governance administrator"
|
|
27
|
+
]
|
|
28
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"audience": "Identity",
|
|
3
|
+
"scopes": [
|
|
4
|
+
{
|
|
5
|
+
"scope": "identity:external_identifier.link",
|
|
6
|
+
"verb": "Identity::ExternalIdentifier.Link",
|
|
7
|
+
"role": "Identity registrar"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"scope": "identity:identity.register",
|
|
11
|
+
"verb": "Identity::Identity.Register",
|
|
12
|
+
"role": "Identity registrar"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"roles": [
|
|
16
|
+
"Identity registrar"
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"audience": "Privacy",
|
|
3
|
+
"scopes": [
|
|
4
|
+
{
|
|
5
|
+
"scope": "privacy:marking.mark",
|
|
6
|
+
"verb": "Privacy::Marking.Mark",
|
|
7
|
+
"role": "Deployment"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"scope": "privacy:marking.unmark",
|
|
11
|
+
"verb": "Privacy::Marking.Unmark",
|
|
12
|
+
"role": "Deployment"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"scope": "privacy:subject_key.issue",
|
|
16
|
+
"verb": "Privacy::SubjectKey.Issue",
|
|
17
|
+
"role": "System"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"scope": "privacy:subject_key.shred",
|
|
21
|
+
"verb": "Privacy::SubjectKey.Shred",
|
|
22
|
+
"role": "Privacy officer"
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"roles": [
|
|
26
|
+
"Deployment",
|
|
27
|
+
"Privacy officer",
|
|
28
|
+
"System"
|
|
29
|
+
]
|
|
30
|
+
}
|
data/lib/hecks/gate/stages.yml
CHANGED
|
@@ -20,8 +20,11 @@
|
|
|
20
20
|
# with `only=`: several of them write generated files and compare, so the stage is not meant to
|
|
21
21
|
# run whole in one checkout.
|
|
22
22
|
pre_push:
|
|
23
|
+
# HECKS_PROCESS_SPECS keeps the examples tagged `process` (they start child processes, so a plain
|
|
24
|
+
# `rspec` leaves them out) in the suite this stage runs.
|
|
23
25
|
env:
|
|
24
26
|
HECKS_ENVIRONMENT: memory
|
|
27
|
+
HECKS_PROCESS_SPECS: "1"
|
|
25
28
|
checks:
|
|
26
29
|
- id: rspec
|
|
27
30
|
title: rspec (parallel)
|