bsdkrun 0.5.0 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d1b2ffdacfec58cee3714b8ca96ecc133604655d423e264444d8f41ea88393e5
4
- data.tar.gz: 848ec7c03c5489a41daf404135c3bbfdcfb126449ecb712ddf920ea7377bc822
3
+ metadata.gz: 86e468aea9e96825db542d4c5e9ddf2fbbc473ddfb4e4b7edfa8ecac48abd685
4
+ data.tar.gz: 86f792adb80c6218763193a256b16c4f307e68063a494718277e4e4b94d3c0b1
5
5
  SHA512:
6
- metadata.gz: a86228909ae198caab0e630bac039179fc115317118f321a1aa193683ee6fb10ccfdc56ae1d05d42cad3ebad712897b5979c0b3b75cd0fe9b64bef0406d1d5ae
7
- data.tar.gz: aef36636aef92124b1c78695648c929bfee4f60e134b9ef0a2d5588693827994e8023f62c91f39cd2d1a19c74e7e1a6a3d99a99c2aca9df51ba53bf03022368e
6
+ metadata.gz: e948aea05cab5bffabb28a50a0fb8df66500ab6b6ead6fe0e8a3ff9f07d981f84f19deac1f775e7c4a7665dd037a711d2c6aba19fb15ee4d1cdb7903f8293c40
7
+ data.tar.gz: 56d75e614b8f78319079302e61cc257cd30a3229e27384d09e6412c9bcb58459a6519d6df4e17a608a0b0fc18e92569e4510fea063bc849b13e60af3a723cea8
data/lib/bsdkrun/ci.rb ADDED
@@ -0,0 +1,204 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "json"
5
+ require "tmpdir"
6
+
7
+ module Bsdkrun
8
+ # CI workflows defined in code instead of YAML.
9
+ #
10
+ # The builder produces exactly the file +bsdkrun ci+ (and tangled's spindle)
11
+ # consumes — {CIWorkflow#yaml} is that file, {CIWorkflow#save} commits it to
12
+ # +.tangled/workflows/+, and {CIWorkflow#run} executes it in a microVM
13
+ # without a file ever touching the repository:
14
+ #
15
+ # Bsdkrun.workflow("test")
16
+ # .on_push("main")
17
+ # .deps("ruby", "bundler")
18
+ # .env("CI_FROM", "sdk")
19
+ # .step("install", "bundle install")
20
+ # .step("test", "bundle exec rspec")
21
+ # .run
22
+ #
23
+ # Code is the source of truth and YAML the wire format, in that order —
24
+ # which is why +save+ writes a generated-file header: a hand-edit there will
25
+ # be overwritten by the next +save+.
26
+ class CIWorkflow
27
+ def initialize(name)
28
+ @name = name
29
+ @engine = "nixery"
30
+ @when = []
31
+ @deps = {}
32
+ @env = {}
33
+ @steps = []
34
+ @clone_depth = nil
35
+ @clone_skip = false
36
+ end
37
+
38
+ # Override the engine (+nixery+ by default).
39
+ def engine(engine)
40
+ @engine = engine
41
+ self
42
+ end
43
+
44
+ # Add a push trigger for the given branches.
45
+ def on_push(*branches)
46
+ @when << [["push"], branches]
47
+ self
48
+ end
49
+
50
+ # Add a pull_request trigger targeting the given branches.
51
+ def on_pull_request(*branches)
52
+ @when << [["pull_request"], branches]
53
+ self
54
+ end
55
+
56
+ # Add a trigger with explicit events.
57
+ def on(events, *branches)
58
+ @when << [events, branches]
59
+ self
60
+ end
61
+
62
+ # Add nixpkgs dependencies — the toolchain the steps run against.
63
+ def deps(*packages)
64
+ (@deps["nixpkgs"] ||= []).concat(packages)
65
+ self
66
+ end
67
+
68
+ # Add dependencies from a custom registry (a flake reference).
69
+ def deps_from(registry, *packages)
70
+ (@deps[registry] ||= []).concat(packages)
71
+ self
72
+ end
73
+
74
+ # Set a workflow-level environment variable.
75
+ def env(key, value)
76
+ @env[key] = value
77
+ self
78
+ end
79
+
80
+ # Append a step; steps run serially in one VM, from the workspace root.
81
+ def step(name, command, env: nil)
82
+ @steps << { name: name, command: command, env: env || {} }
83
+ self
84
+ end
85
+
86
+ # Set the clone depth (default 1).
87
+ def clone_depth(depth)
88
+ @clone_depth = depth
89
+ self
90
+ end
91
+
92
+ # Skip the checkout entirely.
93
+ def skip_clone
94
+ @clone_skip = true
95
+ self
96
+ end
97
+
98
+ # The workflow file name {#save} writes: +<name>.yml+.
99
+ def file_name
100
+ @name.match?(/\.ya?ml\z/) ? @name : "#{@name}.yml"
101
+ end
102
+
103
+ # Render the workflow file. Scalars are emitted as JSON strings — valid
104
+ # YAML by construction — and commands as literal blocks when safe, so the
105
+ # SDK needs no YAML dependency.
106
+ def yaml
107
+ out = []
108
+ q = ->(s) { JSON.generate(s) }
109
+
110
+ unless @when.empty?
111
+ out << "when:"
112
+ @when.each do |events, branches|
113
+ out << " - event: [#{events.map(&q).join(', ')}]"
114
+ if branches.length == 1
115
+ out << " branch: #{q.call(branches[0])}"
116
+ elsif branches.length > 1
117
+ out << " branch: [#{branches.map(&q).join(', ')}]"
118
+ end
119
+ end
120
+ out << ""
121
+ end
122
+
123
+ out << "engine: #{@engine}"
124
+
125
+ unless @deps.empty?
126
+ out << "" << "dependencies:"
127
+ @deps.keys.sort.each do |reg|
128
+ out << " #{q.call(reg)}:"
129
+ @deps[reg].each { |p| out << " - #{q.call(p)}" }
130
+ end
131
+ end
132
+
133
+ unless @env.empty?
134
+ out << "" << "environment:"
135
+ @env.keys.sort.each { |k| out << " #{k}: #{q.call(@env[k])}" }
136
+ end
137
+
138
+ if @clone_skip || @clone_depth
139
+ out << "" << "clone:"
140
+ out << " skip: true" if @clone_skip
141
+ out << " depth: #{@clone_depth}" if @clone_depth
142
+ end
143
+
144
+ out << "" << "steps:"
145
+ @steps.each do |s|
146
+ out << " - name: #{q.call(s[:name])}"
147
+ # Literal blocks read well in a committed file, but cannot carry
148
+ # trailing spaces or carriage returns byte-for-byte; fall back to a
149
+ # JSON string rather than silently altering the command.
150
+ block_safe = !s[:command].empty? &&
151
+ !s[:command].include?("\r") &&
152
+ s[:command].split("\n", -1).all? { |l| l == l.sub(/ +\z/, "") }
153
+ if block_safe
154
+ out << " command: |"
155
+ s[:command].sub(/\n+\z/, "").split("\n", -1).each { |l| out << " #{l}" }
156
+ else
157
+ out << " command: #{q.call(s[:command])}"
158
+ end
159
+ next if s[:env].empty?
160
+
161
+ out << " environment:"
162
+ s[:env].keys.sort.each { |k| out << " #{k}: #{q.call(s[:env][k])}" }
163
+ end
164
+ "#{out.join("\n")}\n"
165
+ end
166
+
167
+ # Write into +<repo>/.tangled/workflows/+ and return the path.
168
+ def save(repo)
169
+ dir = File.join(repo, ".tangled", "workflows")
170
+ FileUtils.mkdir_p(dir)
171
+ path = File.join(dir, file_name)
172
+ File.write(
173
+ path,
174
+ "# Generated by the bsdkrun SDK — edit the code that save()d it instead.\n#{yaml}"
175
+ )
176
+ path
177
+ end
178
+
179
+ # Execute the workflow in a microVM, streaming output. The YAML never
180
+ # touches the repository — it goes to a temp file and +bsdkrun ci run -f+.
181
+ # Raises {CommandFailed} when a step fails.
182
+ def run(dir: nil)
183
+ Dir.mktmpdir("bsdkrun-ci-") do |tmp|
184
+ file = File.join(tmp, file_name)
185
+ File.write(file, yaml)
186
+ args = ["ci", "run", "-f", file]
187
+ args += ["-w", dir] if dir
188
+ ok = Process.spawn_interactive(args)
189
+ unless ok
190
+ raise CommandFailed.new(
191
+ exit_code: 1, stdout: "", stderr: "workflow #{@name} failed",
192
+ command: "bsdkrun ci run"
193
+ )
194
+ end
195
+ end
196
+ nil
197
+ end
198
+ end
199
+
200
+ # Start a CI workflow definition.
201
+ def self.workflow(name)
202
+ CIWorkflow.new(name)
203
+ end
204
+ end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Bsdkrun
4
4
  # The bsdkrun Ruby SDK version.
5
- VERSION = "0.5.0"
5
+ VERSION = "0.6.0"
6
6
  end
data/lib/bsdkrun.rb CHANGED
@@ -6,6 +6,7 @@ require_relative "bsdkrun/binary"
6
6
  require_relative "bsdkrun/process"
7
7
  require_relative "bsdkrun/args"
8
8
  require_relative "bsdkrun/cache"
9
+ require_relative "bsdkrun/ci"
9
10
  require_relative "bsdkrun/filesystem"
10
11
  require_relative "bsdkrun/types"
11
12
  require_relative "bsdkrun/sandbox"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bsdkrun
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tsiry Sandratraina
@@ -52,6 +52,7 @@ files:
52
52
  - lib/bsdkrun/args.rb
53
53
  - lib/bsdkrun/binary.rb
54
54
  - lib/bsdkrun/cache.rb
55
+ - lib/bsdkrun/ci.rb
55
56
  - lib/bsdkrun/client.rb
56
57
  - lib/bsdkrun/errors.rb
57
58
  - lib/bsdkrun/filesystem.rb