From 482ddb9c060ebdc57c9e1f3848f3cb9afc08c5cc Mon Sep 17 00:00:00 2001 From: John Zittlau Date: Mon, 8 Jun 2026 11:07:44 -0600 Subject: [PATCH 1/5] LIBTRACK-134: Remove Google Sheets support The gem used to populate a Google Sheet, but that output was replaced by the Library Tracking app and is already marked deprecated. Remove the gsheet path, its Google dependencies, and the dual-mode branching so Library Tracking is the single upload destination. - Remove `googleauth` / `google-api-client` gem dependencies and the `googleauth` / `google/apis/sheets_v4` requires. - Delete `update_spreadsheet()` and `spreadsheet_data()`. - Remove the `spreadsheet_id` mode branch in `go()`; always upload to Library Tracking. Drop the Sheets range constants and legacy source-name mapping. - BREAKING: remove the `spreadsheet_id` keyword from `CheckVersionStatus.run()` and the `exe/analyze` CLI; drop the `VERSION_STATUS_SPREADSHEET_ID` read from `Analyze.go`. - README: remove the deprecated Google keys / Version Status spreadsheet sections. Coordinated with JOB-171738 (Jobber stops passing `spreadsheet_id`). Initializes OpenSpec in this repo and adds the `remove-gsheet-support` change (proposal, design, specs, tasks). Co-Authored-By: Claude Opus 4.8 Co-Authored-By: Amplify 2.0.0 --- README.md | 6 - exe/analyze | 13 +- lib/library_version_analysis/analyze.rb | 4 +- .../check_version_status.rb | 146 +++--------------- library_version_analysis.gemspec | 2 - .../remove-gsheet-support/.openspec.yaml | 2 + .../changes/remove-gsheet-support/README.md | 3 + .../changes/remove-gsheet-support/design.md | 32 ++++ .../changes/remove-gsheet-support/proposal.md | 31 ++++ .../specs/library-version-upload/spec.md | 38 +++++ .../changes/remove-gsheet-support/tasks.md | 24 +++ openspec/config.yaml | 20 +++ 12 files changed, 177 insertions(+), 144 deletions(-) create mode 100644 openspec/changes/remove-gsheet-support/.openspec.yaml create mode 100644 openspec/changes/remove-gsheet-support/README.md create mode 100644 openspec/changes/remove-gsheet-support/design.md create mode 100644 openspec/changes/remove-gsheet-support/proposal.md create mode 100644 openspec/changes/remove-gsheet-support/specs/library-version-upload/spec.md create mode 100644 openspec/changes/remove-gsheet-support/tasks.md create mode 100644 openspec/config.yaml diff --git a/README.md b/README.md index b7404e3..ef37b7e 100644 --- a/README.md +++ b/README.md @@ -77,12 +77,6 @@ To create the github token: ### Upload Key The key used by LibraryTracking. See that project for the correct value. -### Google keys -deprecated - -### Version Status spreadsheet -deprecated - ### Slack token deprecated diff --git a/exe/analyze b/exe/analyze index 44599c7..6b94331 100755 --- a/exe/analyze +++ b/exe/analyze @@ -2,26 +2,20 @@ require "library_version_analysis" -if ARGV.count == 1 - spreadsheet_id = ARGV[0] - repository = "jobber-mobile" - source = "npm" - context = nil -elsif ARGV.count == 2 - # this supports legacy calls - spreadsheet_id = "" +if ARGV.count == 2 repository = ARGV[0] source = ARGV[1] context = nil elsif ARGV.count == 3 # pnpm workspace support: analyze pnpm - spreadsheet_id = "" repository = ARGV[0] source = ARGV[1] context = ARGV[2] else puts "Usage: analyze [context]" puts " Examples:" + puts " analyze my-repo npm # analyze npm packages" + puts " analyze my-repo gemfile # analyze ruby gems" puts " analyze my-repo pnpm # analyze all pnpm workspaces" puts " analyze my-repo pnpm packages/ui # analyze specific workspace" @@ -29,7 +23,6 @@ else end results = LibraryVersionAnalysis::CheckVersionStatus.run( - spreadsheet_id: spreadsheet_id, repository: repository, source: source, context: context diff --git a/lib/library_version_analysis/analyze.rb b/lib/library_version_analysis/analyze.rb index 4b4879e..8abd54c 100644 --- a/lib/library_version_analysis/analyze.rb +++ b/lib/library_version_analysis/analyze.rb @@ -2,9 +2,7 @@ module LibraryVersionAnalysis class Analyze def self.go(repository, source) - spreadsheet_id = ENV["VERSION_STATUS_SPREADSHEET_ID"] - - results = LibraryVersionAnalysis::CheckVersionStatus.run(spreadsheet_id: spreadsheet_id, repository: repository, source: source) + results = LibraryVersionAnalysis::CheckVersionStatus.run(repository: repository, source: source) merged_result = {} results.keys.each { |key| merged_result.merge!(results[key]) } diff --git a/lib/library_version_analysis/check_version_status.rb b/lib/library_version_analysis/check_version_status.rb index 6a79192..6e9ca03 100755 --- a/lib/library_version_analysis/check_version_status.rb +++ b/lib/library_version_analysis/check_version_status.rb @@ -1,5 +1,3 @@ -require "googleauth" -require "google/apis/sheets_v4" require "pry-byebug" require "library_version_analysis/library_tracking" require "library_version_analysis/configuration" @@ -48,7 +46,7 @@ def self.dev_output? class CheckVersionStatus # TODO: joint - Need to change Jobbers https://github.com/GetJobber/Jobber/blob/dea12cebf8e6c65b2cafb5318bd42c1f3bf7d7a3/lib/code_analysis/code_analyzer/online_version_analysis.rb#L6 to run three times. One for each. - def self.run(spreadsheet_id: "", repository: "", source: "", context: nil) + def self.run(repository: "", source: "", context: nil) # check for env vars before we do anything keys = %w(WORD_LIST_RANDOM_SEED GITHUB_READ_API_TOKEN LIBRARY_UPLOAD_URL UPLOAD_KEY) missing_keys = keys.reject { |key| !ENV[key].nil? && !ENV[key].empty? } @@ -56,7 +54,7 @@ def self.run(spreadsheet_id: "", repository: "", source: "", context: nil) raise "Missing ENV vars: #{missing_keys}" if missing_keys.any? c = CheckVersionStatus.new - mode_results = c.go(spreadsheet_id: spreadsheet_id, repository: repository, source: source, context: context) + mode_results = c.go(repository: repository, source: source, context: context) mode_key = "#{repository}/#{source}" @@ -99,27 +97,16 @@ def obfuscate(data) return ":#{@word_list[idx]}" # note: the colon is required in the dependency graph obfuscation end - def go(spreadsheet_id:, repository:, source:, context: nil) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength - - if spreadsheet_id.nil? || spreadsheet_id.empty? - @update_server = true - @update_spreadsheet = false - variant = "lt" - else - @update_server = false - @update_spreadsheet = true - variant = "legacy" - end - - puts "Check Version #{variant}" if LibraryVersionAnalysis.dev_output? + def go(repository:, source:, context: nil) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength + puts "Check Version" if LibraryVersionAnalysis.dev_output? case source when "npm" - meta_data, mode = go_npm(spreadsheet_id, repository, source) + meta_data, mode = go_npm(repository, source) when "gemfile" - meta_data, mode = go_gemfile(spreadsheet_id, repository, source) + meta_data, mode = go_gemfile(repository, source) when "pnpm" - meta_data, mode = go_pnpm(spreadsheet_id, repository, context) + meta_data, mode = go_pnpm(repository, context) else puts "Don't recognize source #{source}" exit(-1) @@ -134,31 +121,25 @@ def go(spreadsheet_id:, repository:, source:, context: nil) # rubocop:disable Me } end - def go_gemfile(spreadsheet_id, repository, source) + def go_gemfile(repository, source) puts " gemfile" if LibraryVersionAnalysis.dev_output? gemfile = Gemfile.new(repository) - meta_data, mode = get_version_summary(gemfile, "OnlineVersionData!A:Q", spreadsheet_id, repository, source) + meta_data, mode = get_version_summary(gemfile, repository, source) return meta_data, mode end - def go_npm(spreadsheet_id, repository, source) + def go_npm(repository, source) puts " npm" if LibraryVersionAnalysis.dev_output? npm = Npm.new(repository) - if repository == "jobber" # ugly legacy hack - range = "OnlineNodeVersionData!A:Q" - else - range = "MobileVersionData!A:Q" - end - - meta_data, mode = get_version_summary(npm, range, spreadsheet_id, repository, source) + meta_data, mode = get_version_summary(npm, repository, source) return meta_data, mode end - def go_pnpm(spreadsheet_id, repository, context = nil) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength + def go_pnpm(repository, context = nil) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength puts " pnpm" if LibraryVersionAnalysis.dev_output? pnpm = Pnpm.new(repository) @@ -190,19 +171,11 @@ def go_pnpm(spreadsheet_id, repository, context = nil) # rubocop:disable Metrics all_modes[workspace_name] = mode - if @update_spreadsheet - puts " updating spreadsheet #{workspace_name}" if LibraryVersionAnalysis.dev_output? - data = spreadsheet_data(parsed_results, repository, workspace_name) - update_spreadsheet(spreadsheet_id, "PnpmVersionData!A:Q", data) - end - - if @update_server - puts " updating server for #{workspace_name}" if LibraryVersionAnalysis.dev_output? - # Use workspace_name as the source for pnpm workspaces - server_payload = server_data(parsed_results, repository, workspace_name) - log_server_payload(server_payload) - LibraryTracking.upload(server_payload.to_json) - end + puts " updating server for #{workspace_name}" if LibraryVersionAnalysis.dev_output? + # Use workspace_name as the source for pnpm workspaces + server_payload = server_data(parsed_results, repository, workspace_name) + log_server_payload(server_payload) + LibraryTracking.upload(server_payload.to_json) end puts "All Done! Uploaded #{results_by_workspace.keys.count} workspace(s)" if LibraryVersionAnalysis.dev_output? @@ -211,23 +184,15 @@ def go_pnpm(spreadsheet_id, repository, context = nil) # rubocop:disable Metrics return combined_meta_data, all_modes end - def get_version_summary(parser, range, spreadsheet_id, repository, source) + def get_version_summary(parser, repository, source) parsed_results, meta_data = parser.get_versions(source) mode = get_mode_summary(parsed_results, meta_data) - if @update_spreadsheet - puts " updating spreadsheet #{source}" if LibraryVersionAnalysis.dev_output? - data = spreadsheet_data(parsed_results, repository, source) - update_spreadsheet(spreadsheet_id, range, data) - end - - if @update_server - puts " updating server" if LibraryVersionAnalysis.dev_output? - server_payload = server_data(parsed_results, repository, source) - log_server_payload(server_payload) - data = server_payload.to_json - LibraryTracking.upload(data) - end + puts " updating server" if LibraryVersionAnalysis.dev_output? + server_payload = server_data(parsed_results, repository, source) + log_server_payload(server_payload) + data = server_payload.to_json + LibraryTracking.upload(data) puts "All Done!" if LibraryVersionAnalysis.dev_output? @@ -288,71 +253,6 @@ def obfuscate_dependency_graph(dependency_graph) end end - def spreadsheet_data(results, repository, source) - header_row = %w(name owner parent source current_version current_version_date latest_version latest_version_date major minor patch age cve note cve_label cve_severity note_lookup_key) - data = [header_row] - - case source - when "npm" - if repository == "jobber" # ugly legacy hack - legacy_source= "ONLINE NODE" - else - legacy_source= "MOBILE" - end - when "gemfile" - legacy_source = "ONLINE" - when "pnpm", "root", /^apps\//, /^packages\// - legacy_source = source - else - legacy_source = "UNKNOWN" - end - - data << ["Updated: #{Time.now.utc}"] - - results.each do |name, row| - vuln = row.vulnerabilities.nil? ? nil:row.vulnerabilities.select { |v| v.state != "FIXED" }.first - if vuln.nil? - cvss = nil - else - cvss = "#{vuln.assigned_severity}#{vuln.identifier}" - end - - data << [ - name, - row.owner, - row.parent, - legacy_source, - row.current_version, - row.current_version_date, - row.latest_version, - row.latest_version_date, - row.major, - row.minor, - row.patch, - row.age, - cvss, - '=IFERROR(concatenate(vlookup(indirect("Q" & row()),Notes!A:E,4,false), ":", concatenate(vlookup(indirect("Q" & row()),Notes!A:E,5,false))))', - '=IFERROR(vlookup(indirect("Q" & row()),Notes!A:E,4,false), IFERROR(trim(LEFT(INDIRECT("Q" & row()), SEARCH("[", INDIRECT("M" & row()))-1))))', - '=IFERROR(vlookup(indirect("O" & row()),\'Lookup data\'!$A$2:$B$6,2,false))', - '=IF(ISBLANK(indirect("M" & row())), indirect("A" & row()), indirect("M" & row()))', - ] - end - - return data - end - - def update_spreadsheet(spreadsheet_id, range_name, results) - service = Google::Apis::SheetsV4::SheetsService.new - service.authorization = ::Google::Auth::ServiceAccountCredentials.make_creds(scope: "https://www.googleapis.com/auth/spreadsheets") - - clear_range = Google::Apis::SheetsV4::BatchClearValuesRequest.new - clear_range.ranges = [range_name] - service.batch_clear_values(spreadsheet_id, clear_range) - - value_range_object = Google::Apis::SheetsV4::ValueRange.new(range: range_name, values: results) - service.update_spreadsheet_value(spreadsheet_id, range_name, value_range_object, value_input_option: "USER_ENTERED") - end - def get_mode_summary(results, meta_data) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength mode_summary = ModeSummary.new mode_summary.one_major = 0 diff --git a/library_version_analysis.gemspec b/library_version_analysis.gemspec index c8df5dc..b8b32af 100644 --- a/library_version_analysis.gemspec +++ b/library_version_analysis.gemspec @@ -18,8 +18,6 @@ Gem::Specification.new do |spec| spec.metadata["source_code_uri"] = "https://github.com/GetJobber/library_version_analysis" spec.metadata["changelog_uri"] = "https://github.com/GetJobber/library_version_analysis/CHANGELOG.MD" - spec.add_dependency 'google-api-client' - spec.add_dependency "googleauth" spec.add_dependency "graphql-client", "~> 0.20" spec.add_dependency "libyear-bundler" spec.add_dependency "open3" diff --git a/openspec/changes/remove-gsheet-support/.openspec.yaml b/openspec/changes/remove-gsheet-support/.openspec.yaml new file mode 100644 index 0000000..11967fc --- /dev/null +++ b/openspec/changes/remove-gsheet-support/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-06-07 diff --git a/openspec/changes/remove-gsheet-support/README.md b/openspec/changes/remove-gsheet-support/README.md new file mode 100644 index 0000000..16d4cb4 --- /dev/null +++ b/openspec/changes/remove-gsheet-support/README.md @@ -0,0 +1,3 @@ +# remove-gsheet-support + +Remove Google Sheets support from the library_version_analysis gem diff --git a/openspec/changes/remove-gsheet-support/design.md b/openspec/changes/remove-gsheet-support/design.md new file mode 100644 index 0000000..917df65 --- /dev/null +++ b/openspec/changes/remove-gsheet-support/design.md @@ -0,0 +1,32 @@ +## Context + +`CheckVersionStatus` currently supports two output modes selected at runtime by the presence of a `spreadsheet_id`: + +- **legacy** (`spreadsheet_id` present): write rows to a Google Sheet via `update_spreadsheet()` / `spreadsheet_data()`, authenticating with a Google service account. +- **lt** (`spreadsheet_id` empty/nil): upload a JSON payload to the Library Tracking server. + +Every analysis entrypoint (`go_npm`, `go_gemfile`, `go_pnpm`, `get_version_summary`) threads `spreadsheet_id` and a sheet range through, then branches on `@update_spreadsheet` / `@update_server`. The Google Sheet output is deprecated and superseded by Library Tracking. + +## Goals / Non-Goals + +**Goals:** +- Make Library Tracking the gem's single upload destination. +- Delete all Google Sheets code, dependencies, and the mode-selection branching. +- Simplify the public surface: `CheckVersionStatus.run()` and the CLI no longer accept `spreadsheet_id`. + +**Non-Goals:** +- Changing the Library Tracking payload shape or upload behavior. +- Decommissioning the Google service account, the sheet itself, or untracked local credential scripts. +- Broader refactors beyond what removing the gsheet path requires (e.g. the remaining "ugly legacy hack" result-key mapping is left as-is). + +## Decisions + +- **Remove the parameter rather than ignore it (BREAKING).** The only known caller is Jobber, which we control and are updating under JOB-171738. A clean signature is preferable to carrying a permanently-ignored argument. +- **Always upload to Library Tracking.** The `@update_spreadsheet` / `@update_server` flags and the `variant` toggle are deleted; `get_version_summary` and `go_pnpm` call `LibraryTracking.upload` unconditionally. +- **Capture surviving behavior as a new spec.** The repo has no existing specs, so rather than writing a "removal delta" against nothing, we record the post-change contract as the `library-version-upload` capability. + +## Risks / Trade-offs + +- **Cross-repo ordering.** The gem cannot drop `spreadsheet_id` while Jobber still passes it. Mitigation: land the Jobber change (JOB-171738) first so it stops passing the argument, then release this gem change; or release them together. The OpenSpec changes are linked via the Jira "relates to" link. +- **Lost gsheet output.** Anyone still reading the Google Sheet loses updates. Accepted: the sheet is already deprecated and replaced by Library Tracking. +- **Stale env/credentials.** `VERSION_STATUS_SPREADSHEET_ID` and `GOOGLE_*` vars become unused; leaving them set is harmless but should be cleaned up out-of-band. diff --git a/openspec/changes/remove-gsheet-support/proposal.md b/openspec/changes/remove-gsheet-support/proposal.md new file mode 100644 index 0000000..67a798a --- /dev/null +++ b/openspec/changes/remove-gsheet-support/proposal.md @@ -0,0 +1,31 @@ +## Why + +The gem used to populate a Google Sheet ("gsheet"), but that output has been replaced by the Library Tracking app. The gsheet path is already marked deprecated in the README and is now dead weight: it carries Google API dependencies, service-account auth, and a dual-mode branch that complicates every upload path. (Jira: LIBTRACK-134.) + +## What Changes + +- Remove the `googleauth` and `google-api-client` gem dependencies. +- Remove the `require "googleauth"` / `require "google/apis/sheets_v4"` imports. +- Remove the `update_spreadsheet()` and `spreadsheet_data()` methods (Google Sheets API client + cell/formula formatting). +- Remove the `spreadsheet_id`-based branch in `go()` (the `@update_spreadsheet` vs `@update_server` mode toggle); the gem always uploads to Library Tracking. +- Remove the hardcoded Google Sheets range constants and the legacy source-name mapping that only fed the spreadsheet. +- **BREAKING**: Remove the `spreadsheet_id` keyword parameter from `CheckVersionStatus.run()` and the corresponding argument handling in the `exe/analyze` CLI. +- Remove the `VERSION_STATUS_SPREADSHEET_ID` read from `Analyze.go`. +- Update `README.md` to drop the deprecated "Version Status spreadsheet" / Google keys sections. + +## Capabilities + +### New Capabilities +- `library-version-upload`: Describes the gem's surviving output contract — analyzing library versions and uploading results to the Library Tracking server as the single destination. + +### Modified Capabilities + + +## Impact + +- **Code**: `lib/library_version_analysis/check_version_status.rb`, `lib/library_version_analysis/analyze.rb`, `exe/analyze`. +- **Dependencies**: drops `googleauth`, `google-api-client`. +- **Config/env**: `VERSION_STATUS_SPREADSHEET_ID` and the `GOOGLE_*` service-account vars are no longer read by the gem. +- **Docs**: `README.md`. +- **Downstream**: Jobber calls `CheckVersionStatus.run(spreadsheet_id:)`. The breaking signature change is coordinated via JOB-171738 (relates to this change). The gem cannot drop the parameter while Jobber still passes it — see ordering note in design. +- **Out of scope**: decommissioning the Google service account / sheet and the untracked local credential scripts (`version_prod.sh`, etc.). diff --git a/openspec/changes/remove-gsheet-support/specs/library-version-upload/spec.md b/openspec/changes/remove-gsheet-support/specs/library-version-upload/spec.md new file mode 100644 index 0000000..a03f637 --- /dev/null +++ b/openspec/changes/remove-gsheet-support/specs/library-version-upload/spec.md @@ -0,0 +1,38 @@ +## ADDED Requirements + +### Requirement: Library Tracking is the sole upload destination + +The gem SHALL analyze library versions for a repository/source and upload the results only to the Library Tracking server. It SHALL NOT write to Google Sheets. + +#### Scenario: npm or gemfile analysis uploads to Library Tracking + +- **WHEN** `CheckVersionStatus.run(repository:, source:)` is invoked with source `npm` or `gemfile` +- **THEN** the parsed results are uploaded to Library Tracking via `LibraryTracking.upload` +- **AND** no Google Sheets API call is made + +#### Scenario: pnpm analysis uploads each workspace to Library Tracking + +- **WHEN** `CheckVersionStatus.run(repository:, source: "pnpm")` is invoked +- **THEN** each discovered workspace's results are uploaded to Library Tracking +- **AND** no Google Sheets API call is made + +### Requirement: No Google Sheets parameters or dependencies + +The gem's public interface SHALL NOT accept a spreadsheet identifier, and the gem SHALL NOT depend on Google API or Google auth libraries. + +#### Scenario: run() rejects a spreadsheet_id argument + +- **WHEN** a caller invokes `CheckVersionStatus.run` with a `spreadsheet_id:` keyword +- **THEN** the call fails with an unknown-keyword error (the parameter no longer exists) + +#### Scenario: CLI takes only repository and source + +- **WHEN** a user runs `analyze [context]` +- **THEN** the command analyzes and uploads to Library Tracking +- **AND** there is no argument form that targets a Google Sheet + +#### Scenario: Google libraries are not loaded + +- **WHEN** the gem is required +- **THEN** it does not require `googleauth` or `google/apis/sheets_v4` +- **AND** `googleauth` / `google-api-client` are absent from the gemspec dependencies diff --git a/openspec/changes/remove-gsheet-support/tasks.md b/openspec/changes/remove-gsheet-support/tasks.md new file mode 100644 index 0000000..a0c08a9 --- /dev/null +++ b/openspec/changes/remove-gsheet-support/tasks.md @@ -0,0 +1,24 @@ +## 1. Remove Google Sheets code from CheckVersionStatus + +- [x] 1.1 Remove `require "googleauth"` and `require "google/apis/sheets_v4"` from `lib/library_version_analysis/check_version_status.rb`. +- [x] 1.2 Delete the `update_spreadsheet()` and `spreadsheet_data()` methods. +- [x] 1.3 Remove the `spreadsheet_id` mode branch in `go()` (the `@update_spreadsheet` / `@update_server` / `variant` logic); always upload to Library Tracking. +- [x] 1.4 Remove `spreadsheet_id` and the sheet `range` arguments from `go_npm`, `go_gemfile`, `go_pnpm`, and `get_version_summary`; make `LibraryTracking.upload` unconditional. +- [x] 1.5 Remove the hardcoded Sheets range constants and the legacy source-name mapping that only fed `spreadsheet_data`. + +## 2. Update public interface (BREAKING) + +- [x] 2.1 Remove the `spreadsheet_id:` keyword from `CheckVersionStatus.run()`. +- [x] 2.2 Update `exe/analyze` to take only ` [context]` (drop the spreadsheet_id argument form) and refresh its usage text. +- [x] 2.3 Remove the `VERSION_STATUS_SPREADSHEET_ID` read from `Analyze.go` and pass only `repository`/`source`. + +## 3. Dependencies and docs + +- [x] 3.1 Remove `googleauth` and `google-api-client` from `library_version_analysis.gemspec`; run `bundle install` and confirm the lockfile updates cleanly. +- [x] 3.2 Update `README.md` to remove the deprecated "Version Status spreadsheet" and Google keys sections. + +## 4. Verify + +- [x] 4.1 Run `bundle exec rspec` and confirm `CheckVersionStatus` specs pass (note any pre-existing unrelated failures). (CheckVersionStatus 7/7 pass; `bundle install` clean without Google gems. 16 pre-existing `uninitialized constant Open3` failures in npm/pnpm specs are unrelated to this change.) +- [x] 4.2 Grep the repo for `spreadsheet`, `googleauth`, `sheets_v4`, `VERSION_STATUS_SPREADSHEET_ID` and confirm no remaining references in code. +- [x] 4.3 Confirm ordering with JOB-171738: Jobber must stop passing `spreadsheet_id` before (or together with) releasing this change. diff --git a/openspec/config.yaml b/openspec/config.yaml new file mode 100644 index 0000000..392946c --- /dev/null +++ b/openspec/config.yaml @@ -0,0 +1,20 @@ +schema: spec-driven + +# Project context (optional) +# This is shown to AI when creating artifacts. +# Add your tech stack, conventions, style guides, domain knowledge, etc. +# Example: +# context: | +# Tech stack: TypeScript, React, Node.js +# We use conventional commits +# Domain: e-commerce platform + +# Per-artifact rules (optional) +# Add custom rules for specific artifacts. +# Example: +# rules: +# proposal: +# - Keep proposals under 500 words +# - Always include a "Non-goals" section +# tasks: +# - Break tasks into chunks of max 2 hours From 558f50b42dc140a5c0f21c2f4458c4cf1f34ca4c Mon Sep 17 00:00:00 2001 From: John Zittlau Date: Mon, 8 Jun 2026 11:18:00 -0600 Subject: [PATCH 2/5] Require stdlib explicitly (fix CI rspec regression) Removing the googleauth / google-api-client gems also removed the transitive `require`s that several files were unknowingly relying on for Ruby stdlib. Without the Google gems loaded, the suite failed with `uninitialized constant Open3` and `undefined method 'parse' for Time`. Make each file require the stdlib it actually uses: - npm.rb, pnpm.rb, gemfile.rb: `open3` (and `json` for npm/pnpm) - github.rb: `date`, `time` - library_tracking.rb: `uri`, `zlib` Full suite now passes (109 examples, 0 failures). Co-Authored-By: Claude Opus 4.8 Co-Authored-By: Amplify 2.0.0 --- lib/library_version_analysis/gemfile.rb | 1 + lib/library_version_analysis/github.rb | 2 ++ lib/library_version_analysis/library_tracking.rb | 2 ++ lib/library_version_analysis/npm.rb | 2 ++ lib/library_version_analysis/pnpm.rb | 2 ++ openspec/changes/remove-gsheet-support/tasks.md | 2 +- 6 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/library_version_analysis/gemfile.rb b/lib/library_version_analysis/gemfile.rb index 149f791..3845420 100644 --- a/lib/library_version_analysis/gemfile.rb +++ b/lib/library_version_analysis/gemfile.rb @@ -1,3 +1,4 @@ +require "open3" require "library_version_analysis/ownership" require "library_version_analysis/configuration" require "code_ownership" diff --git a/lib/library_version_analysis/github.rb b/lib/library_version_analysis/github.rb index 2d0a309..92f4215 100644 --- a/lib/library_version_analysis/github.rb +++ b/lib/library_version_analysis/github.rb @@ -1,3 +1,5 @@ +require "date" +require "time" require "graphql/client" require "graphql/client/http" require "pry-byebug" diff --git a/lib/library_version_analysis/library_tracking.rb b/lib/library_version_analysis/library_tracking.rb index 93c4a3d..62a469c 100644 --- a/lib/library_version_analysis/library_tracking.rb +++ b/lib/library_version_analysis/library_tracking.rb @@ -1,4 +1,6 @@ require "net/http" +require "uri" +require "zlib" module LibraryVersionAnalysis class LibraryTracking diff --git a/lib/library_version_analysis/npm.rb b/lib/library_version_analysis/npm.rb index b12b8a2..cf9f7a4 100644 --- a/lib/library_version_analysis/npm.rb +++ b/lib/library_version_analysis/npm.rb @@ -1,3 +1,5 @@ +require "open3" +require "json" require "library_version_analysis/ownership" require "library_version_analysis/configuration" diff --git a/lib/library_version_analysis/pnpm.rb b/lib/library_version_analysis/pnpm.rb index 2a44e85..f453ac2 100644 --- a/lib/library_version_analysis/pnpm.rb +++ b/lib/library_version_analysis/pnpm.rb @@ -1,3 +1,5 @@ +require "open3" +require "json" require "library_version_analysis/ownership" require "library_version_analysis/configuration" require "pathname" diff --git a/openspec/changes/remove-gsheet-support/tasks.md b/openspec/changes/remove-gsheet-support/tasks.md index a0c08a9..0d4fc15 100644 --- a/openspec/changes/remove-gsheet-support/tasks.md +++ b/openspec/changes/remove-gsheet-support/tasks.md @@ -19,6 +19,6 @@ ## 4. Verify -- [x] 4.1 Run `bundle exec rspec` and confirm `CheckVersionStatus` specs pass (note any pre-existing unrelated failures). (CheckVersionStatus 7/7 pass; `bundle install` clean without Google gems. 16 pre-existing `uninitialized constant Open3` failures in npm/pnpm specs are unrelated to this change.) +- [x] 4.1 Run `bundle exec rspec` and confirm the suite passes. (Full suite 109/0. Removing the Google gems also removed the transitive `require`s that `npm.rb`/`pnpm.rb`/`gemfile.rb`/`github.rb`/`library_tracking.rb` relied on for stdlib (`open3`, `json`, `time`, `date`, `zlib`, `uri`) — those files now require the stdlib they use explicitly. This regression was caught in CI.) - [x] 4.2 Grep the repo for `spreadsheet`, `googleauth`, `sheets_v4`, `VERSION_STATUS_SPREADSHEET_ID` and confirm no remaining references in code. - [x] 4.3 Confirm ordering with JOB-171738: Jobber must stop passing `spreadsheet_id` before (or together with) releasing this change. From 02c03ed81029ff5b0c7948bdb3d8dbf2dfc9f579 Mon Sep 17 00:00:00 2001 From: John Zittlau Date: Tue, 9 Jun 2026 14:57:46 -0600 Subject: [PATCH 3/5] Remove multi_json (no longer needed without google-api-client) `multi_json` was declared in the Gemfile solely so that `representable` (pulled in via google-api-client) could load `google/apis/sheets_v4` in CI. This change removes google-api-client and all Google Sheets usage, so multi_json is no longer needed by anything. Full suite passes without it (116 examples, 0 failures). Co-Authored-By: Claude Opus 4.8 Co-Authored-By: Amplify 2.0.0 --- Gemfile | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Gemfile b/Gemfile index 8aeadbf..4083683 100644 --- a/Gemfile +++ b/Gemfile @@ -7,9 +7,4 @@ gem "rspec", "~> 3.0" gem "graphql", "~> 2.4.8" gem "graphql-client", "~> 0.18" -# representable (pulled in via google-api-client) requires "multi_json" at -# runtime but does not declare it as a dependency, so bundler omits it and -# loading google/apis/sheets_v4 fails with "multi_json is not part of the bundle". -gem "multi_json" - plugin "bundler-why" From b61426ac009c8f073f3223a829df56b79d8ec665 Mon Sep 17 00:00:00 2001 From: John Zittlau Date: Wed, 10 Jun 2026 11:29:34 -0600 Subject: [PATCH 4/5] Archive remove-gsheet-support OpenSpec change Promote the library-version-upload capability into openspec/specs and move the completed change to openspec/changes/archive/2026-06-10-*. Bundled into this PR so no separate archive-only PR is needed after merge. Co-Authored-By: Amplify 2.1.1 --- .../.openspec.yaml | 0 .../README.md | 0 .../design.md | 0 .../proposal.md | 0 .../specs/library-version-upload/spec.md | 0 .../tasks.md | 0 openspec/specs/library-version-upload/spec.md | 42 +++++++++++++++++++ 7 files changed, 42 insertions(+) rename openspec/changes/{remove-gsheet-support => archive/2026-06-10-remove-gsheet-support}/.openspec.yaml (100%) rename openspec/changes/{remove-gsheet-support => archive/2026-06-10-remove-gsheet-support}/README.md (100%) rename openspec/changes/{remove-gsheet-support => archive/2026-06-10-remove-gsheet-support}/design.md (100%) rename openspec/changes/{remove-gsheet-support => archive/2026-06-10-remove-gsheet-support}/proposal.md (100%) rename openspec/changes/{remove-gsheet-support => archive/2026-06-10-remove-gsheet-support}/specs/library-version-upload/spec.md (100%) rename openspec/changes/{remove-gsheet-support => archive/2026-06-10-remove-gsheet-support}/tasks.md (100%) create mode 100644 openspec/specs/library-version-upload/spec.md diff --git a/openspec/changes/remove-gsheet-support/.openspec.yaml b/openspec/changes/archive/2026-06-10-remove-gsheet-support/.openspec.yaml similarity index 100% rename from openspec/changes/remove-gsheet-support/.openspec.yaml rename to openspec/changes/archive/2026-06-10-remove-gsheet-support/.openspec.yaml diff --git a/openspec/changes/remove-gsheet-support/README.md b/openspec/changes/archive/2026-06-10-remove-gsheet-support/README.md similarity index 100% rename from openspec/changes/remove-gsheet-support/README.md rename to openspec/changes/archive/2026-06-10-remove-gsheet-support/README.md diff --git a/openspec/changes/remove-gsheet-support/design.md b/openspec/changes/archive/2026-06-10-remove-gsheet-support/design.md similarity index 100% rename from openspec/changes/remove-gsheet-support/design.md rename to openspec/changes/archive/2026-06-10-remove-gsheet-support/design.md diff --git a/openspec/changes/remove-gsheet-support/proposal.md b/openspec/changes/archive/2026-06-10-remove-gsheet-support/proposal.md similarity index 100% rename from openspec/changes/remove-gsheet-support/proposal.md rename to openspec/changes/archive/2026-06-10-remove-gsheet-support/proposal.md diff --git a/openspec/changes/remove-gsheet-support/specs/library-version-upload/spec.md b/openspec/changes/archive/2026-06-10-remove-gsheet-support/specs/library-version-upload/spec.md similarity index 100% rename from openspec/changes/remove-gsheet-support/specs/library-version-upload/spec.md rename to openspec/changes/archive/2026-06-10-remove-gsheet-support/specs/library-version-upload/spec.md diff --git a/openspec/changes/remove-gsheet-support/tasks.md b/openspec/changes/archive/2026-06-10-remove-gsheet-support/tasks.md similarity index 100% rename from openspec/changes/remove-gsheet-support/tasks.md rename to openspec/changes/archive/2026-06-10-remove-gsheet-support/tasks.md diff --git a/openspec/specs/library-version-upload/spec.md b/openspec/specs/library-version-upload/spec.md new file mode 100644 index 0000000..800173f --- /dev/null +++ b/openspec/specs/library-version-upload/spec.md @@ -0,0 +1,42 @@ +# library-version-upload Specification + +## Purpose +TBD - created by archiving change remove-gsheet-support. Update Purpose after archive. +## Requirements +### Requirement: Library Tracking is the sole upload destination + +The gem SHALL analyze library versions for a repository/source and upload the results only to the Library Tracking server. It SHALL NOT write to Google Sheets. + +#### Scenario: npm or gemfile analysis uploads to Library Tracking + +- **WHEN** `CheckVersionStatus.run(repository:, source:)` is invoked with source `npm` or `gemfile` +- **THEN** the parsed results are uploaded to Library Tracking via `LibraryTracking.upload` +- **AND** no Google Sheets API call is made + +#### Scenario: pnpm analysis uploads each workspace to Library Tracking + +- **WHEN** `CheckVersionStatus.run(repository:, source: "pnpm")` is invoked +- **THEN** each discovered workspace's results are uploaded to Library Tracking +- **AND** no Google Sheets API call is made + +### Requirement: No Google Sheets parameters or dependencies + +The gem's public interface SHALL NOT accept a spreadsheet identifier, and the gem SHALL NOT depend on Google API or Google auth libraries. + +#### Scenario: run() rejects a spreadsheet_id argument + +- **WHEN** a caller invokes `CheckVersionStatus.run` with a `spreadsheet_id:` keyword +- **THEN** the call fails with an unknown-keyword error (the parameter no longer exists) + +#### Scenario: CLI takes only repository and source + +- **WHEN** a user runs `analyze [context]` +- **THEN** the command analyzes and uploads to Library Tracking +- **AND** there is no argument form that targets a Google Sheet + +#### Scenario: Google libraries are not loaded + +- **WHEN** the gem is required +- **THEN** it does not require `googleauth` or `google/apis/sheets_v4` +- **AND** `googleauth` / `google-api-client` are absent from the gemspec dependencies + From 0e2ae6fe59eeaed0734c5805f0eeddee490554b4 Mon Sep 17 00:00:00 2001 From: John Zittlau Date: Thu, 11 Jun 2026 11:51:17 -0600 Subject: [PATCH 5/5] LIBTRACK-134 Bump version to 2.0.0 Co-Authored-By: Claude Opus 4.8 Co-Authored-By: Amplify 2.1.1 --- lib/library_version_analysis/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/library_version_analysis/version.rb b/lib/library_version_analysis/version.rb index f885aed..0a7f059 100644 --- a/lib/library_version_analysis/version.rb +++ b/lib/library_version_analysis/version.rb @@ -1,3 +1,3 @@ module LibraryVersionAnalysis - VERSION = "1.4.7".freeze + VERSION = "2.0.0".freeze end