featureparity 0.0.8 → 0.0.9
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/fp/client.rb +25 -0
- data/lib/fp/commands/help.rb +4 -0
- data/lib/fp/commands/surfaces.rb +80 -1
- data/lib/fp/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 20c34d118adff460a3de1eaaa15f66718593100ae0261f9dc064f63b65d8b3f2
|
|
4
|
+
data.tar.gz: e14ab1bf7bbf6c646f42e0b9a9291653e072f47055b8dff8ccf2294d10effa7a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 248bf8d80901ed3a4075e44e501169bd37803dc4fad78b3b342cfd79307a4649b824664a0b7ce5013f3fd323b2f64ed7deaa2318813389604e21d8c212cd2c36
|
|
7
|
+
data.tar.gz: 1e22a8d5b38f77581693dd4cb0d912aedb0d09afa8cdffaa96332eca3afc1d9c62893ff2c0a09e9ca7f4d44d46fd6126d71b915cb3bfa357099c2a3726ce2006
|
data/lib/fp/client.rb
CHANGED
|
@@ -27,6 +27,31 @@ module Fp
|
|
|
27
27
|
get("/api/projects/#{project_id}/surfaces")
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
# GET /projects/:id/surfaces/:surface_id
|
|
31
|
+
def get_surface(project_id, surface_id)
|
|
32
|
+
get("/api/projects/#{project_id}/surfaces/#{surface_id}")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Find a single surface by its key within a project.
|
|
36
|
+
#
|
|
37
|
+
# Agents speak surface *keys* ("customer_android"), not the KSUID the surface is
|
|
38
|
+
# stored under, so — the same way find_requirement_by_slug resolves a slug — list the
|
|
39
|
+
# project's surfaces and match on key. Returns the same { ok:, data: } envelope the
|
|
40
|
+
# rest of the client speaks, with a 404 when no surface has that key.
|
|
41
|
+
def find_surface_by_key(project_id, key)
|
|
42
|
+
result = list_surfaces(project_id)
|
|
43
|
+
return result unless result[:ok]
|
|
44
|
+
|
|
45
|
+
surfaces = result[:data]['surfaces'] || []
|
|
46
|
+
surface = surfaces.find { |s| s['key'] == key }
|
|
47
|
+
|
|
48
|
+
if surface
|
|
49
|
+
{ ok: true, data: { 'surface' => surface }, status: 200 }
|
|
50
|
+
else
|
|
51
|
+
{ ok: false, error: "Surface '#{key}' not found in project", status: 404 }
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
30
55
|
# POST /projects/:id/surfaces
|
|
31
56
|
def create_surface(project_id, params)
|
|
32
57
|
post("/api/projects/#{project_id}/surfaces", params)
|
data/lib/fp/commands/help.rb
CHANGED
|
@@ -15,6 +15,7 @@ module Fp
|
|
|
15
15
|
init --project <slug> Onboard this repo: config, CI gate, agent docs
|
|
16
16
|
projects List projects you have access to
|
|
17
17
|
surfaces --project <slug> List surfaces for a project
|
|
18
|
+
surfaces show <key> --project ... View a single surface's details
|
|
18
19
|
surfaces add <key> --project ... Create a surface
|
|
19
20
|
list --project <slug> List active requirements
|
|
20
21
|
show <slug> --project <slug> Show requirement details
|
|
@@ -121,6 +122,9 @@ module Fp
|
|
|
121
122
|
# List the surfaces a project tracks (requirements may only name these)
|
|
122
123
|
fp surfaces --project stowzilla
|
|
123
124
|
|
|
125
|
+
# View a single surface's details (owner, repo, kind, test globs)
|
|
126
|
+
fp surfaces show customer_android --project stowzilla
|
|
127
|
+
|
|
124
128
|
# Create a surface (agents can do this — no need to wait on the web app)
|
|
125
129
|
fp surfaces add customer_android --project stowzilla \\
|
|
126
130
|
--name "Customer Android App" \\
|
data/lib/fp/commands/surfaces.rb
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
3
5
|
module Fp
|
|
4
6
|
module Commands
|
|
5
7
|
# fp surfaces --project <slug> - list available surfaces for a project
|
|
8
|
+
# fp surfaces show <key> --project <slug> - view a single surface's details
|
|
6
9
|
# fp surfaces add <key> --project <slug> - create a surface
|
|
7
10
|
#
|
|
8
11
|
# `add` exists because a requirement can only name surfaces that already exist, so an
|
|
@@ -24,8 +27,11 @@ module Fp
|
|
|
24
27
|
def run(args)
|
|
25
28
|
# Subcommand dispatch. Bare `fp surfaces --project X` stays a list, so existing
|
|
26
29
|
# callers (and the docs) keep working.
|
|
27
|
-
|
|
30
|
+
case args.first
|
|
31
|
+
when 'add'
|
|
28
32
|
add(args[1..])
|
|
33
|
+
when 'show'
|
|
34
|
+
show(args[1..])
|
|
29
35
|
else
|
|
30
36
|
list(args)
|
|
31
37
|
end
|
|
@@ -59,6 +65,31 @@ module Fp
|
|
|
59
65
|
end
|
|
60
66
|
end
|
|
61
67
|
|
|
68
|
+
def show(args)
|
|
69
|
+
opts, positional = parse_flags(args)
|
|
70
|
+
require_project!(opts)
|
|
71
|
+
|
|
72
|
+
key = positional.first
|
|
73
|
+
if key.nil? || key.strip.empty?
|
|
74
|
+
output.error('Usage: fp surfaces show <key> --project <slug>')
|
|
75
|
+
exit 1
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
project_id = resolve_project_id(opts[:project])
|
|
79
|
+
result = client.find_surface_by_key(project_id, key)
|
|
80
|
+
|
|
81
|
+
unless result[:ok]
|
|
82
|
+
output.error(result[:error], status: result[:status])
|
|
83
|
+
exit 1
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
surface = result[:data]['surface'] || {}
|
|
87
|
+
|
|
88
|
+
output.success(result[:data]) do
|
|
89
|
+
print_surface(surface, project: opts[:project])
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
62
93
|
def add(args)
|
|
63
94
|
opts, positional = parse_flags(args, ADD_FLAGS)
|
|
64
95
|
require_project!(opts)
|
|
@@ -108,6 +139,54 @@ module Fp
|
|
|
108
139
|
output.error("Invalid #{flag} '#{value}'. Must be one of: #{allowed.join(', ')}")
|
|
109
140
|
exit 1
|
|
110
141
|
end
|
|
142
|
+
|
|
143
|
+
# Human-readable detail view for a single surface. Mirrors the shape of
|
|
144
|
+
# `fp show` for requirements: title underline, then the fields that matter.
|
|
145
|
+
def print_surface(surface, project:)
|
|
146
|
+
name = surface['name'] || surface['key'].to_s
|
|
147
|
+
puts name
|
|
148
|
+
puts '=' * name.length
|
|
149
|
+
puts
|
|
150
|
+
puts "Key: #{surface['key']}"
|
|
151
|
+
puts "Project: #{project}"
|
|
152
|
+
puts "Kind: #{surface['kind'] || '(none)'}"
|
|
153
|
+
puts "Audience: #{surface['audience'] || '(none)'}"
|
|
154
|
+
puts "Repo: #{surface['repo'] || '(none)'}"
|
|
155
|
+
puts "Branch: #{surface['default_branch'] || '(none)'}" if surface.key?('default_branch')
|
|
156
|
+
|
|
157
|
+
owner = surface['owner_id']
|
|
158
|
+
team = surface['team']
|
|
159
|
+
puts "Owner: #{owner && !owner.to_s.empty? ? owner : '(unassigned)'}"
|
|
160
|
+
puts "Team: #{team && !team.to_s.empty? ? team : '(none)'}"
|
|
161
|
+
|
|
162
|
+
globs = normalize_test_globs(surface['test_globs'])
|
|
163
|
+
unless globs.empty?
|
|
164
|
+
puts
|
|
165
|
+
puts 'Test Globs:'
|
|
166
|
+
globs.each { |g| puts " - #{g}" }
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
puts
|
|
170
|
+
puts "ID: #{surface['id']}" if surface['id']
|
|
171
|
+
puts "Created: #{surface['created_at']}" if surface['created_at']
|
|
172
|
+
puts "Updated: #{surface['updated_at']}" if surface['updated_at']
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# test_globs may arrive as a JSON string or an array; normalize to an array for
|
|
176
|
+
# display. Blank/unparseable → empty (the server falls back to kind defaults).
|
|
177
|
+
def normalize_test_globs(value)
|
|
178
|
+
case value
|
|
179
|
+
when Array then value
|
|
180
|
+
when String
|
|
181
|
+
stripped = value.strip
|
|
182
|
+
return [] if stripped.empty?
|
|
183
|
+
|
|
184
|
+
parsed = JSON.parse(stripped) rescue nil
|
|
185
|
+
parsed.is_a?(Array) ? parsed : []
|
|
186
|
+
else
|
|
187
|
+
[]
|
|
188
|
+
end
|
|
189
|
+
end
|
|
111
190
|
end
|
|
112
191
|
end
|
|
113
192
|
end
|
data/lib/fp/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: featureparity
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stowzilla
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-18 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: net-http
|