issuer 0.1.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 +7 -0
- data/.rspec +3 -0
- data/.vale/config/vocabularies/issuer/accept.txt +63 -0
- data/.vale/config/vocabularies/issuer/reject.txt +21 -0
- data/.vale.ini +42 -0
- data/Dockerfile +43 -0
- data/LICENSE +21 -0
- data/README.adoc +539 -0
- data/Rakefile +70 -0
- data/bin/console +0 -0
- data/bin/issuer +13 -0
- data/bin/setup +0 -0
- data/examples/README.adoc +56 -0
- data/examples/advanced-stub-example.yml +50 -0
- data/examples/basic-example.yml +33 -0
- data/examples/minimal-example.yml +9 -0
- data/examples/new-project-issues.yml +162 -0
- data/examples/validation-test.yml +8 -0
- data/exe/issuer +5 -0
- data/issuer.gemspec +43 -0
- data/lib/issuer/apis/github/client.rb +124 -0
- data/lib/issuer/cache.rb +197 -0
- data/lib/issuer/cli.rb +241 -0
- data/lib/issuer/issue.rb +393 -0
- data/lib/issuer/ops.rb +281 -0
- data/lib/issuer/sites/base.rb +109 -0
- data/lib/issuer/sites/factory.rb +31 -0
- data/lib/issuer/sites/github.rb +248 -0
- data/lib/issuer/version.rb +21 -0
- data/lib/issuer.rb +238 -0
- data/scripts/build.sh +40 -0
- data/scripts/lint-docs.sh +64 -0
- data/scripts/manage-runs.rb +175 -0
- data/scripts/pre-commit-template.sh +54 -0
- data/scripts/publish.sh +92 -0
- data/scripts/setup-vale.sh +59 -0
- data/specs/tests/README.adoc +451 -0
- data/specs/tests/check-github-connectivity.sh +130 -0
- data/specs/tests/cleanup-github-tests.sh +374 -0
- data/specs/tests/github-api/01-auth-connection.yml +21 -0
- data/specs/tests/github-api/02-basic-issues.yml +90 -0
- data/specs/tests/github-api/03-milestone-tests.yml +58 -0
- data/specs/tests/github-api/04-label-tests.yml +98 -0
- data/specs/tests/github-api/05-assignment-tests.yml +55 -0
- data/specs/tests/github-api/06-automation-tests.yml +102 -0
- data/specs/tests/github-api/07-error-tests.yml +29 -0
- data/specs/tests/github-api/08-complex-tests.yml +197 -0
- data/specs/tests/github-api/config.yml.example +17 -0
- data/specs/tests/rspec/cli_spec.rb +127 -0
- data/specs/tests/rspec/issue_spec.rb +184 -0
- data/specs/tests/rspec/issuer_spec.rb +5 -0
- data/specs/tests/rspec/ops_spec.rb +124 -0
- data/specs/tests/rspec/spec_helper.rb +54 -0
- data/specs/tests/run-github-api-tests.sh +424 -0
- metadata +200 -0
data/lib/issuer.rb
ADDED
@@ -0,0 +1,238 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "issuer/version"
|
4
|
+
require_relative "issuer/issue"
|
5
|
+
require_relative "issuer/apis/github/client"
|
6
|
+
require_relative "issuer/sites/base"
|
7
|
+
require_relative "issuer/sites/github"
|
8
|
+
require_relative "issuer/sites/factory"
|
9
|
+
require_relative "issuer/ops"
|
10
|
+
require_relative "issuer/cache"
|
11
|
+
require_relative "issuer/cli"
|
12
|
+
|
13
|
+
##
|
14
|
+
# = Issuer: Bulk GitHub Issue Creator
|
15
|
+
#
|
16
|
+
# Issuer is a Ruby gem that allows you to define all your work tickets in one place
|
17
|
+
# using the IMYML (Issue Management YAML-based Modeling Language) format, apply defaults,
|
18
|
+
# and post them to GitHub Issues (and other platforms) in bulk.
|
19
|
+
#
|
20
|
+
# == Features
|
21
|
+
#
|
22
|
+
# * Bulk issue creation from a single YAML file
|
23
|
+
# * Dry-run mode for testing without creating issues
|
24
|
+
# * Automatic milestone and label creation
|
25
|
+
# * Configurable defaults and label application logic
|
26
|
+
# * Environment variable support for authentication
|
27
|
+
# * Issue validation with helpful error messages
|
28
|
+
# * Extensible architecture for multiple platforms (GitHub, JIRA, GitLab, etc.)
|
29
|
+
# * Run logging and artifact tracking for cleanup operations
|
30
|
+
#
|
31
|
+
# == Quick Start
|
32
|
+
#
|
33
|
+
# # Create issues from an IMYML file
|
34
|
+
# processor = Issuer::Processor.new
|
35
|
+
# results = processor.process_file('issues.yml',
|
36
|
+
# proj: 'myorg/myrepo',
|
37
|
+
# dry_run: false)
|
38
|
+
#
|
39
|
+
# # Access individual components
|
40
|
+
# site = Issuer::Sites::Factory.create(:github)
|
41
|
+
# issues = Issuer::Issue.from_array(yaml_data['issues'], defaults)
|
42
|
+
#
|
43
|
+
# == Extensibility
|
44
|
+
#
|
45
|
+
# The gem is designed for extensibility:
|
46
|
+
#
|
47
|
+
# * Create new site adapters by subclassing {Issuer::Sites::Base}
|
48
|
+
# * Extend the IMYML format by customizing {Issuer::Ops} methods
|
49
|
+
# * Add custom validation logic to {Issuer::Issue}
|
50
|
+
# * Track operations with {Issuer::Cache}
|
51
|
+
#
|
52
|
+
# @see https://github.com/DocOps/issuer Project Homepage
|
53
|
+
# @see Issuer::Sites::Base For creating new platform adapters
|
54
|
+
# @see Issuer::Issue For the core issue model
|
55
|
+
# @see Issuer::Ops For IMYML processing operations
|
56
|
+
#
|
57
|
+
module Issuer
|
58
|
+
|
59
|
+
##
|
60
|
+
# Standard error class for all Issuer-related errors
|
61
|
+
class Error < StandardError; end
|
62
|
+
|
63
|
+
##
|
64
|
+
# Main processor class that provides a clean API for external usage.
|
65
|
+
# This is the recommended entry point for programmatic access to Issuer functionality.
|
66
|
+
#
|
67
|
+
# @example Basic usage
|
68
|
+
# processor = Issuer::Processor.new
|
69
|
+
# results = processor.process_file('issues.yml', proj: 'myorg/myrepo')
|
70
|
+
#
|
71
|
+
# @example With custom site
|
72
|
+
# site = Issuer::Sites::Factory.create(:github, token: 'custom-token')
|
73
|
+
# processor = Issuer::Processor.new(site: site)
|
74
|
+
# results = processor.process_data(yaml_data, proj: 'myorg/myrepo')
|
75
|
+
#
|
76
|
+
class Processor
|
77
|
+
|
78
|
+
##
|
79
|
+
# Initialize a new processor
|
80
|
+
#
|
81
|
+
# @param site [Issuer::Sites::Base, nil] Custom site adapter. If nil, will auto-detect.
|
82
|
+
# @param cache [Boolean] Whether to enable run tracking and caching (default: true)
|
83
|
+
#
|
84
|
+
def initialize(site: nil, cache: true)
|
85
|
+
@site = site
|
86
|
+
@cache_enabled = cache
|
87
|
+
end
|
88
|
+
|
89
|
+
##
|
90
|
+
# Process an IMYML file and create issues
|
91
|
+
#
|
92
|
+
# @param file_path [String] Path to the IMYML YAML file
|
93
|
+
# @param proj [String, nil] Target repository (org/repo format)
|
94
|
+
# @param dry_run [Boolean] If true, validate and show what would be created without API calls
|
95
|
+
# @param automation_options [Hash] Options for automatic resource creation
|
96
|
+
# @option automation_options [Boolean] :auto_versions Automatically create missing milestones
|
97
|
+
# @option automation_options [Boolean] :auto_tags Automatically create missing labels
|
98
|
+
# @option automation_options [Boolean] :auto_metadata Automatically create all missing metadata
|
99
|
+
#
|
100
|
+
# @return [Hash] Results including created issues, milestones, labels, and run metadata
|
101
|
+
# @raise [Issuer::Error] If file cannot be read or processed
|
102
|
+
#
|
103
|
+
# @example
|
104
|
+
# results = processor.process_file('issues.yml',
|
105
|
+
# proj: 'myorg/myrepo',
|
106
|
+
# dry_run: false,
|
107
|
+
# automation_options: { auto_metadata: true })
|
108
|
+
# puts "Created #{results[:issues_created]} issues"
|
109
|
+
#
|
110
|
+
def process_file(file_path, proj: nil, dry_run: false, automation_options: {})
|
111
|
+
require 'yaml'
|
112
|
+
|
113
|
+
unless File.exist?(file_path)
|
114
|
+
raise Error, "File not found: #{file_path}"
|
115
|
+
end
|
116
|
+
|
117
|
+
begin
|
118
|
+
raw_data = YAML.load_file(file_path)
|
119
|
+
rescue => e
|
120
|
+
raise Error, "Could not parse YAML file: #{file_path}\n#{e.message}"
|
121
|
+
end
|
122
|
+
|
123
|
+
process_data(raw_data, proj: proj, dry_run: dry_run, automation_options: automation_options)
|
124
|
+
end
|
125
|
+
|
126
|
+
##
|
127
|
+
# Process IMYML data structure and create issues
|
128
|
+
#
|
129
|
+
# @param data [Hash] Parsed IMYML data structure
|
130
|
+
# @param proj [String, nil] Target repository (org/repo format)
|
131
|
+
# @param dry_run [Boolean] If true, validate and show what would be created without API calls
|
132
|
+
# @param automation_options [Hash] Options for automatic resource creation
|
133
|
+
#
|
134
|
+
# @return [Hash] Results including created issues, milestones, labels, and run metadata
|
135
|
+
# @raise [Issuer::Error] If data is invalid or processing fails
|
136
|
+
#
|
137
|
+
def process_data(data, proj: nil, dry_run: false, automation_options: {})
|
138
|
+
# Extract metadata and issues
|
139
|
+
meta = data['$meta'] || {}
|
140
|
+
issues_data = data['issues'] || data
|
141
|
+
|
142
|
+
unless issues_data.is_a?(Array)
|
143
|
+
raise Error, 'No issues array found (root or under "issues")'
|
144
|
+
end
|
145
|
+
|
146
|
+
# Build defaults
|
147
|
+
defaults = (meta['defaults'] || {}).dup
|
148
|
+
defaults['proj'] = meta['proj'] if meta['proj']
|
149
|
+
|
150
|
+
# Determine target repository
|
151
|
+
target_repo = proj || meta['proj'] || ENV['ISSUER_REPO']
|
152
|
+
if target_repo.nil? && !dry_run
|
153
|
+
raise Error, 'No target repo specified. Use proj parameter, $meta.proj, or ISSUER_REPO environment variable.'
|
154
|
+
end
|
155
|
+
|
156
|
+
# Process issues
|
157
|
+
issues = Ops.process_issues_data(issues_data, defaults)
|
158
|
+
valid_issues = issues.select(&:valid?)
|
159
|
+
invalid_issues = issues.reject(&:valid?)
|
160
|
+
|
161
|
+
# Report validation errors
|
162
|
+
invalid_issues.each_with_index do |issue, idx|
|
163
|
+
puts "â ī¸ Skipping invalid issue: #{issue.validation_errors.join(', ')}"
|
164
|
+
end
|
165
|
+
|
166
|
+
if dry_run
|
167
|
+
return perform_dry_run(valid_issues, target_repo)
|
168
|
+
else
|
169
|
+
return perform_live_run(valid_issues, target_repo, automation_options)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
private
|
174
|
+
|
175
|
+
def get_site
|
176
|
+
@site ||= Sites::Factory.create(Sites::Factory.default_site)
|
177
|
+
end
|
178
|
+
|
179
|
+
def perform_dry_run(issues, repo)
|
180
|
+
site = Sites::Factory.create('github', token: 'dry-run-token')
|
181
|
+
|
182
|
+
puts "đ§Ē DRY RUN - No issues will be created"
|
183
|
+
puts "đ Target repository: #{repo}" if repo
|
184
|
+
puts "đ Would create #{issues.length} issues:"
|
185
|
+
puts
|
186
|
+
|
187
|
+
issues.each_with_index do |issue, idx|
|
188
|
+
params = site.convert_issue_to_site_params(issue, repo, dry_run: true)
|
189
|
+
puts "#{idx + 1}. #{params[:title]}"
|
190
|
+
puts " Labels: #{params[:labels].join(', ')}" if params[:labels]&.any?
|
191
|
+
puts " Assignee: #{params[:assignee]}" if params[:assignee]
|
192
|
+
puts " Milestone: #{params[:milestone]}" if params[:milestone]
|
193
|
+
puts
|
194
|
+
end
|
195
|
+
|
196
|
+
{
|
197
|
+
dry_run: true,
|
198
|
+
issues_planned: issues.length,
|
199
|
+
target_repo: repo,
|
200
|
+
valid_issues: issues.length
|
201
|
+
}
|
202
|
+
end
|
203
|
+
|
204
|
+
def perform_live_run(issues, repo, automation_options)
|
205
|
+
site = get_site
|
206
|
+
|
207
|
+
# Start run tracking if caching enabled
|
208
|
+
run_id = if @cache_enabled
|
209
|
+
Cache.start_run(issues_planned: issues.length, target_repo: repo)
|
210
|
+
else
|
211
|
+
nil
|
212
|
+
end
|
213
|
+
|
214
|
+
begin
|
215
|
+
# Validate and prepare resources (milestones, labels)
|
216
|
+
Ops.validate_and_prepare_resources(site, repo, issues, automation_options, run_id) unless issues.empty?
|
217
|
+
|
218
|
+
# Create issues
|
219
|
+
processed_count = site.post_issues(repo, issues, run_id)
|
220
|
+
|
221
|
+
# Complete run tracking
|
222
|
+
Cache.complete_run(run_id, processed_count) if run_id
|
223
|
+
|
224
|
+
{
|
225
|
+
dry_run: false,
|
226
|
+
issues_created: processed_count,
|
227
|
+
issues_planned: issues.length,
|
228
|
+
target_repo: repo,
|
229
|
+
run_id: run_id
|
230
|
+
}
|
231
|
+
rescue => e
|
232
|
+
# Mark run as failed
|
233
|
+
Cache.fail_run(run_id, e.message) if run_id
|
234
|
+
raise
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
data/scripts/build.sh
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
# Release automation script for Ruby gem projects
|
3
|
+
|
4
|
+
set -e
|
5
|
+
|
6
|
+
# Load common build functions
|
7
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
8
|
+
source "$SCRIPT_DIR/lib/build-common.sh"
|
9
|
+
|
10
|
+
# Project-specific configuration
|
11
|
+
PROJECT_NAME="issuer"
|
12
|
+
DOCKER_ORG="docopslab"
|
13
|
+
|
14
|
+
echo -e "${GREEN}đ ${PROJECT_NAME} Release Build Script${NC}"
|
15
|
+
echo "=================================="
|
16
|
+
|
17
|
+
# Validation
|
18
|
+
check_project_root
|
19
|
+
check_git_clean
|
20
|
+
check_main_branch
|
21
|
+
check_bundle_installed
|
22
|
+
|
23
|
+
# Run tests
|
24
|
+
run_rspec_tests
|
25
|
+
test_cli_functionality
|
26
|
+
|
27
|
+
# Get current version
|
28
|
+
current_version=$(get_current_version)
|
29
|
+
echo -e "${GREEN}đ Current version: $current_version${NC}"
|
30
|
+
|
31
|
+
# Build and test gem
|
32
|
+
build_gem
|
33
|
+
gem_file=$(test_built_gem)
|
34
|
+
|
35
|
+
# Build Docker image using the docker-specific script
|
36
|
+
echo -e "${YELLOW}đŗ Building Docker image...${NC}"
|
37
|
+
"$SCRIPT_DIR/build-docker.sh" 2>&1 | grep -E "(Building|Testing|successfully|Docker image:)" || true
|
38
|
+
|
39
|
+
# Show final success message
|
40
|
+
show_build_success "$current_version" "$gem_file"
|
@@ -0,0 +1,64 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
# Vale linting script for the issuer project
|
3
|
+
|
4
|
+
set -e
|
5
|
+
|
6
|
+
echo "đ Running Vale linter on issuer project..."
|
7
|
+
|
8
|
+
# Colors for output
|
9
|
+
RED='\033[0;31m'
|
10
|
+
GREEN='\033[0;32m'
|
11
|
+
YELLOW='\033[1;33m'
|
12
|
+
NC='\033[0m' # No Color
|
13
|
+
|
14
|
+
# Check if Vale is installed
|
15
|
+
if ! command -v vale &> /dev/null; then
|
16
|
+
echo -e "${RED}â Vale is not installed. Please install Vale first.${NC}"
|
17
|
+
echo " You can install it from: https://github.com/errata-ai/vale"
|
18
|
+
exit 1
|
19
|
+
fi
|
20
|
+
|
21
|
+
# Check if .vale.ini exists
|
22
|
+
if [[ ! -f ".vale.ini" ]]; then
|
23
|
+
echo -e "${RED}â .vale.ini not found in current directory${NC}"
|
24
|
+
echo " Please run this script from the project root."
|
25
|
+
exit 1
|
26
|
+
fi
|
27
|
+
|
28
|
+
# Default to checking all relevant files if no arguments provided
|
29
|
+
if [[ $# -eq 0 ]]; then
|
30
|
+
FILES=(
|
31
|
+
"README.adoc"
|
32
|
+
"lib/examples/*.md"
|
33
|
+
"specs/tests/*.md"
|
34
|
+
"*.adoc"
|
35
|
+
"docs/*.adoc"
|
36
|
+
)
|
37
|
+
else
|
38
|
+
FILES=("$@")
|
39
|
+
fi
|
40
|
+
|
41
|
+
echo "đ Files to check: ${FILES[*]}"
|
42
|
+
echo ""
|
43
|
+
|
44
|
+
# Run Vale
|
45
|
+
EXIT_CODE=0
|
46
|
+
for pattern in "${FILES[@]}"; do
|
47
|
+
# Use find to expand patterns and handle missing files gracefully
|
48
|
+
while IFS= read -r -d '' file; do
|
49
|
+
if [[ -f "$file" ]]; then
|
50
|
+
echo -e "${YELLOW}đ Checking: $file${NC}"
|
51
|
+
if ! vale "$file"; then
|
52
|
+
EXIT_CODE=1
|
53
|
+
fi
|
54
|
+
fi
|
55
|
+
done < <(find . -name "$pattern" -print0 2>/dev/null)
|
56
|
+
done
|
57
|
+
|
58
|
+
if [[ $EXIT_CODE -eq 0 ]]; then
|
59
|
+
echo -e "${GREEN}â
Vale linting completed successfully!${NC}"
|
60
|
+
else
|
61
|
+
echo -e "${RED}â Vale found issues. Please review and fix them.${NC}"
|
62
|
+
fi
|
63
|
+
|
64
|
+
exit $EXIT_CODE
|
@@ -0,0 +1,175 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Run management utility for issuer CLI
|
5
|
+
# Lists and manages cached runs in .issuer/logs/
|
6
|
+
|
7
|
+
require 'bundler/setup'
|
8
|
+
$LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
|
9
|
+
require 'issuer/cache'
|
10
|
+
|
11
|
+
def show_help
|
12
|
+
puts <<~HELP
|
13
|
+
Issuer Run Management Utility
|
14
|
+
|
15
|
+
Usage:
|
16
|
+
ruby scripts/manage-runs.rb [command]
|
17
|
+
|
18
|
+
Commands:
|
19
|
+
list List all cached runs
|
20
|
+
list --recent List recent runs only (last 10)
|
21
|
+
show RUN_ID Show detailed information for a specific run
|
22
|
+
clean-logs Remove all log files (use with caution)
|
23
|
+
|
24
|
+
Examples:
|
25
|
+
ruby scripts/manage-runs.rb list
|
26
|
+
ruby scripts/manage-runs.rb show run_20250711_180124_97d1f1f3
|
27
|
+
ruby scripts/manage-runs.rb list --recent
|
28
|
+
HELP
|
29
|
+
end
|
30
|
+
|
31
|
+
def list_runs recent_only = false
|
32
|
+
runs = Issuer::Cache.list_runs
|
33
|
+
runs = runs.take(10) if recent_only
|
34
|
+
|
35
|
+
if runs.empty?
|
36
|
+
puts "No cached runs found."
|
37
|
+
return
|
38
|
+
end
|
39
|
+
|
40
|
+
puts "Cached Runs#{recent_only ? ' (Recent)' : ''}:"
|
41
|
+
puts "=" * 60
|
42
|
+
|
43
|
+
runs.each do |run|
|
44
|
+
puts "#{run[:run_id]} - #{run[:status].upcase}"
|
45
|
+
puts " Started: #{run[:started_at]}"
|
46
|
+
|
47
|
+
if run[:metadata] && run[:metadata][:issues_planned]
|
48
|
+
puts " Issues planned: #{run[:metadata][:issues_planned]}"
|
49
|
+
end
|
50
|
+
|
51
|
+
if run[:status] == 'completed'
|
52
|
+
puts " Completed: #{run[:completed_at]}"
|
53
|
+
puts " Artifacts: #{run[:summary][:issues_created]} issues, #{run[:summary][:milestones_created]} milestones, #{run[:summary][:labels_created]} labels"
|
54
|
+
puts " Processed: #{run[:summary][:issues_processed]} issues"
|
55
|
+
elsif run[:status] == 'failed'
|
56
|
+
puts " Failed: #{run[:failed_at] || 'unknown time'}"
|
57
|
+
puts " Error: #{run[:error] || 'no error message'}"
|
58
|
+
end
|
59
|
+
puts ""
|
60
|
+
end
|
61
|
+
|
62
|
+
puts "Total: #{runs.length} runs"
|
63
|
+
puts "Use 'ruby scripts/manage-runs.rb show RUN_ID' for detailed view"
|
64
|
+
end
|
65
|
+
|
66
|
+
def show_run run_id
|
67
|
+
run_data = Issuer::Cache.get_run(run_id)
|
68
|
+
unless run_data
|
69
|
+
puts "Error: Run #{run_id} not found."
|
70
|
+
return
|
71
|
+
end
|
72
|
+
|
73
|
+
puts "Run Details: #{run_id}"
|
74
|
+
puts "=" * 60
|
75
|
+
puts "Status: #{run_data[:status]}"
|
76
|
+
puts "Started: #{run_data[:started_at]}"
|
77
|
+
|
78
|
+
if run_data[:completed_at]
|
79
|
+
puts "Completed: #{run_data[:completed_at]}"
|
80
|
+
end
|
81
|
+
|
82
|
+
if run_data[:failed_at]
|
83
|
+
puts "Failed: #{run_data[:failed_at]}"
|
84
|
+
puts "Error: #{run_data[:error]}" if run_data[:error]
|
85
|
+
end
|
86
|
+
|
87
|
+
if run_data[:metadata]
|
88
|
+
puts ""
|
89
|
+
puts "Metadata:"
|
90
|
+
run_data[:metadata].each do |key, value|
|
91
|
+
puts " #{key}: #{value}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
if run_data[:artifacts]
|
96
|
+
puts ""
|
97
|
+
puts "Artifacts Created:"
|
98
|
+
|
99
|
+
[:issues, :milestones, :labels].each do |type|
|
100
|
+
artifacts = run_data[:artifacts][type.to_s] || run_data[:artifacts][type] || []
|
101
|
+
next if artifacts.empty?
|
102
|
+
|
103
|
+
puts " #{type.to_s.capitalize} (#{artifacts.length}):"
|
104
|
+
artifacts.each do |artifact|
|
105
|
+
case type
|
106
|
+
when :issues
|
107
|
+
puts " ##{artifact[:number] || artifact['number']} - #{artifact[:title] || artifact['title']}"
|
108
|
+
puts " #{artifact[:url] || artifact['url']}"
|
109
|
+
when :milestones
|
110
|
+
puts " ##{artifact[:number] || artifact['number']} - #{artifact[:title] || artifact['title']}"
|
111
|
+
puts " #{artifact[:url] || artifact['url']}"
|
112
|
+
when :labels
|
113
|
+
puts " #{artifact[:name] || artifact['name']} (#{artifact[:color] || artifact['color']})"
|
114
|
+
puts " #{artifact[:url] || artifact['url']}"
|
115
|
+
end
|
116
|
+
puts ""
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
if run_data[:summary]
|
122
|
+
puts "Summary:"
|
123
|
+
run_data[:summary].each do |key, value|
|
124
|
+
puts " #{key}: #{value}"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def clean_logs
|
130
|
+
logs_dir = Issuer::Cache.logs_dir
|
131
|
+
unless Dir.exist?(logs_dir)
|
132
|
+
puts "No logs directory found."
|
133
|
+
return
|
134
|
+
end
|
135
|
+
|
136
|
+
log_files = Dir.glob(File.join(logs_dir, '*.json'))
|
137
|
+
if log_files.empty?
|
138
|
+
puts "No log files found."
|
139
|
+
return
|
140
|
+
end
|
141
|
+
|
142
|
+
puts "Found #{log_files.length} log files."
|
143
|
+
print "Are you sure you want to delete all log files? [y/N]: "
|
144
|
+
response = STDIN.gets.chomp.downcase
|
145
|
+
|
146
|
+
unless ['y', 'yes'].include?(response)
|
147
|
+
puts "Cancelled."
|
148
|
+
return
|
149
|
+
end
|
150
|
+
|
151
|
+
log_files.each { |f| File.delete(f) }
|
152
|
+
puts "Deleted #{log_files.length} log files."
|
153
|
+
end
|
154
|
+
|
155
|
+
# Main execution
|
156
|
+
case ARGV[0]
|
157
|
+
when 'list'
|
158
|
+
recent_only = ARGV.include?('--recent')
|
159
|
+
list_runs(recent_only)
|
160
|
+
when 'show'
|
161
|
+
if ARGV[1]
|
162
|
+
show_run(ARGV[1])
|
163
|
+
else
|
164
|
+
puts "Error: Please specify a run ID"
|
165
|
+
puts "Usage: ruby scripts/manage-runs.rb show RUN_ID"
|
166
|
+
end
|
167
|
+
when 'clean-logs'
|
168
|
+
clean_logs
|
169
|
+
when nil, '--help', '-h'
|
170
|
+
show_help
|
171
|
+
else
|
172
|
+
puts "Error: Unknown command '#{ARGV[0]}'"
|
173
|
+
puts ""
|
174
|
+
show_help
|
175
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
# Pre-commit hook for Vale linting
|
3
|
+
|
4
|
+
set -e
|
5
|
+
|
6
|
+
echo "đ Running Vale pre-commit check..."
|
7
|
+
|
8
|
+
# Check if Vale is installed
|
9
|
+
if ! command -v vale &> /dev/null; then
|
10
|
+
echo "â ī¸ Vale not found. Skipping documentation linting."
|
11
|
+
echo " Install Vale to enable documentation quality checks."
|
12
|
+
exit 0
|
13
|
+
fi
|
14
|
+
|
15
|
+
# Get list of staged files that we care about (excluding copilot instructions)
|
16
|
+
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(md|adoc|asciidoc)$' | grep -v '^\.github/copilot-instructions\.md$' || true)
|
17
|
+
|
18
|
+
if [[ -z "$STAGED_FILES" ]]; then
|
19
|
+
echo "âšī¸ No documentation files staged for commit."
|
20
|
+
exit 0
|
21
|
+
fi
|
22
|
+
|
23
|
+
echo "đ Checking staged documentation files:"
|
24
|
+
echo "$STAGED_FILES"
|
25
|
+
|
26
|
+
# Run Vale on staged files
|
27
|
+
TEMP_DIR=$(mktemp -d)
|
28
|
+
EXIT_CODE=0
|
29
|
+
|
30
|
+
for file in $STAGED_FILES; do
|
31
|
+
if [[ -f "$file" ]]; then
|
32
|
+
# Copy staged version to temp location
|
33
|
+
git show :"$file" > "$TEMP_DIR/$(basename "$file")"
|
34
|
+
|
35
|
+
echo "đ Linting: $file"
|
36
|
+
if ! vale "$TEMP_DIR/$(basename "$file")"; then
|
37
|
+
EXIT_CODE=1
|
38
|
+
fi
|
39
|
+
fi
|
40
|
+
done
|
41
|
+
|
42
|
+
# Cleanup
|
43
|
+
rm -rf "$TEMP_DIR"
|
44
|
+
|
45
|
+
if [[ $EXIT_CODE -ne 0 ]]; then
|
46
|
+
echo ""
|
47
|
+
echo "â Vale found documentation issues in staged files."
|
48
|
+
echo " Please fix the issues above before committing."
|
49
|
+
echo " Or use 'git commit --no-verify' to bypass this check."
|
50
|
+
exit 1
|
51
|
+
fi
|
52
|
+
|
53
|
+
echo "â
Documentation quality check passed!"
|
54
|
+
exit 0
|
data/scripts/publish.sh
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
# Manual publishing script for Ruby gem projects
|
3
|
+
|
4
|
+
set -e
|
5
|
+
|
6
|
+
# Parse command line arguments
|
7
|
+
DRY_RUN=false
|
8
|
+
while [[ $# -gt 0 ]]; do
|
9
|
+
case $1 in
|
10
|
+
--dry-run)
|
11
|
+
DRY_RUN=true
|
12
|
+
shift
|
13
|
+
;;
|
14
|
+
*)
|
15
|
+
echo "Unknown option: $1"
|
16
|
+
echo "Usage: $0 [--dry-run]"
|
17
|
+
exit 1
|
18
|
+
;;
|
19
|
+
esac
|
20
|
+
done
|
21
|
+
|
22
|
+
# Load common build functions
|
23
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
24
|
+
source "$SCRIPT_DIR/lib/build-common.sh"
|
25
|
+
|
26
|
+
# Project-specific configuration
|
27
|
+
PROJECT_NAME="issuer"
|
28
|
+
DOCKER_ORG="docopslab"
|
29
|
+
|
30
|
+
echo -e "${GREEN}đĻ ${PROJECT_NAME} Publishing Script${NC}"
|
31
|
+
echo "=============================="
|
32
|
+
|
33
|
+
# Check for required environment variables (skip in dry-run mode)
|
34
|
+
if [ "$DRY_RUN" = false ]; then
|
35
|
+
if [ -z "$RUBYGEMS_API_KEY" ]; then
|
36
|
+
echo -e "${RED}â Error: RUBYGEMS_API_KEY environment variable not set${NC}"
|
37
|
+
echo "Get your API key from https://rubygems.org/profile/edit"
|
38
|
+
echo "Then run: export RUBYGEMS_API_KEY=your_key_here"
|
39
|
+
exit 1
|
40
|
+
fi
|
41
|
+
|
42
|
+
if [ -z "$DOCKERHUB_USERNAME" ] || [ -z "$DOCKERHUB_TOKEN" ]; then
|
43
|
+
echo -e "${RED}â Error: Docker Hub credentials not set${NC}"
|
44
|
+
echo "Set DOCKERHUB_USERNAME and DOCKERHUB_TOKEN environment variables"
|
45
|
+
exit 1
|
46
|
+
fi
|
47
|
+
fi
|
48
|
+
|
49
|
+
# Get current version
|
50
|
+
current_version=$(get_current_version)
|
51
|
+
gem_file="pkg/${PROJECT_NAME}-$current_version.gem"
|
52
|
+
|
53
|
+
# Check if gem file exists
|
54
|
+
if [ ! -f "$gem_file" ]; then
|
55
|
+
echo -e "${RED}â Error: Gem file $gem_file not found${NC}"
|
56
|
+
echo "Run ./scripts/build.sh first"
|
57
|
+
exit 1
|
58
|
+
fi
|
59
|
+
|
60
|
+
if [ "$DRY_RUN" = true ]; then
|
61
|
+
echo -e "${YELLOW}đ Dry run mode enabled. The following actions would be performed:${NC}"
|
62
|
+
echo "- Publish to RubyGems: gem push $gem_file"
|
63
|
+
echo "- Login to Docker Hub and push Docker images"
|
64
|
+
else
|
65
|
+
# Publish to RubyGems
|
66
|
+
echo -e "${YELLOW}đ Publishing to RubyGems...${NC}"
|
67
|
+
mkdir -p ~/.gem
|
68
|
+
echo ":rubygems_api_key: $RUBYGEMS_API_KEY" > ~/.gem/credentials
|
69
|
+
chmod 0600 ~/.gem/credentials
|
70
|
+
gem push "$gem_file"
|
71
|
+
echo -e "${GREEN}â
Published to RubyGems successfully${NC}"
|
72
|
+
|
73
|
+
# Login to Docker Hub
|
74
|
+
echo -e "${YELLOW}đŗ Logging in to Docker Hub...${NC}"
|
75
|
+
echo "$DOCKERHUB_TOKEN" | docker login -u "$DOCKERHUB_USERNAME" --password-stdin
|
76
|
+
|
77
|
+
# Push Docker images
|
78
|
+
echo -e "${YELLOW}đŗ Pushing Docker images...${NC}"
|
79
|
+
docker push ${DOCKER_ORG}/${PROJECT_NAME}:$current_version
|
80
|
+
docker push ${DOCKER_ORG}/${PROJECT_NAME}:latest
|
81
|
+
echo -e "${GREEN}â
Pushed Docker images successfully${NC}"
|
82
|
+
fi
|
83
|
+
|
84
|
+
echo
|
85
|
+
echo -e "${GREEN}đ Publishing completed successfully!${NC}"
|
86
|
+
echo "=============================="
|
87
|
+
echo -e "Gem: ${YELLOW}https://rubygems.org/gems/${PROJECT_NAME}/versions/$current_version${NC}"
|
88
|
+
echo -e "Docker: ${YELLOW}https://hub.docker.com/r/${DOCKER_ORG}/${PROJECT_NAME}${NC}"
|
89
|
+
echo
|
90
|
+
echo "Verify installation:"
|
91
|
+
echo " gem install ${PROJECT_NAME}"
|
92
|
+
echo " docker pull ${DOCKER_ORG}/${PROJECT_NAME}:$current_version"
|