├── cucumber.yml ├── spec ├── spec_helper.rb ├── puppet_blacksmith │ ├── forge_live_spec.rb │ ├── forge_shared.rb │ ├── forge_spec.rb │ ├── git_spec.rb │ └── modulefile_spec.rb └── data │ ├── maestrodev-test │ └── metadata.json │ ├── metadata-no-author.json │ ├── metadata.json │ ├── metadata-different-author.json │ └── response.json ├── lib ├── puppet_blacksmith │ ├── version.rb │ ├── error.rb │ ├── modulefile.rb │ ├── git.rb │ ├── version_helper.rb │ ├── rake_tasks.rb │ └── forge.rb └── puppet_blacksmith.rb ├── features ├── support │ └── env.rb ├── push.feature ├── module.feature ├── update_dependency.feature ├── git.feature └── update_version.feature ├── .rubocop.yml ├── .gitignore ├── Gemfile ├── .github ├── dependabot.yml ├── release.yml └── workflows │ ├── test.yml │ └── release.yml ├── puppet-blacksmith.gemspec ├── Rakefile ├── HISTORY.md ├── .rubocop_todo.yml ├── README.md ├── CHANGELOG.md └── LICENSE /cucumber.yml: -------------------------------------------------------------------------------- 1 | --- 2 | default: --tags "not @skip" 3 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'puppet_blacksmith' 2 | -------------------------------------------------------------------------------- /lib/puppet_blacksmith/version.rb: -------------------------------------------------------------------------------- 1 | module Blacksmith 2 | VERSION = '9.0.0' 3 | end 4 | -------------------------------------------------------------------------------- /lib/puppet_blacksmith/error.rb: -------------------------------------------------------------------------------- 1 | module Blacksmith 2 | class Error < RuntimeError 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /features/support/env.rb: -------------------------------------------------------------------------------- 1 | require 'aruba/cucumber' 2 | 3 | Before do 4 | @aruba_timeout_seconds = 30 5 | end 6 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | --- 2 | inherit_from: .rubocop_todo.yml 3 | 4 | inherit_gem: 5 | voxpupuli-rubocop: rubocop.yml 6 | 7 | Layout/LineLength: 8 | Max: 206 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Gemfile.lock 2 | pkg/ 3 | tmp/ 4 | .bundle/ 5 | .rspec 6 | *.tar.gz 7 | .ruby-gemset 8 | .ruby-version 9 | *.code-workspace 10 | .vendor/ 11 | -------------------------------------------------------------------------------- /lib/puppet_blacksmith.rb: -------------------------------------------------------------------------------- 1 | require 'puppet_blacksmith/version' 2 | require 'puppet_blacksmith/error' 3 | require 'puppet_blacksmith/forge' 4 | require 'puppet_blacksmith/git' 5 | require 'puppet_blacksmith/modulefile' 6 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | 5 | group :release, optional: true do 6 | gem 'faraday-retry', '~> 2.1', require: false 7 | gem 'github_changelog_generator', '~> 1.16.4', require: false 8 | end 9 | -------------------------------------------------------------------------------- /spec/puppet_blacksmith/forge_live_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require_relative 'forge_shared' 3 | require 'fileutils' 4 | 5 | # Run tests against the real Forge staging API 6 | describe 'Blacksmith::Forge', :live do 7 | include_context 'forge' 8 | let(:username) { nil } 9 | 10 | describe 'push' do 11 | before { create_tarball } 12 | 13 | it_behaves_like 'forge_push' 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # raise PRs for gem updates 4 | - package-ecosystem: bundler 5 | directory: "/" 6 | schedule: 7 | interval: daily 8 | time: "13:00" 9 | open-pull-requests-limit: 10 10 | 11 | # Maintain dependencies for GitHub Actions 12 | - package-ecosystem: github-actions 13 | directory: "/" 14 | schedule: 15 | interval: daily 16 | time: "13:00" 17 | open-pull-requests-limit: 10 18 | -------------------------------------------------------------------------------- /spec/data/maestrodev-test/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "maestrodev-test", 3 | "version": "", 4 | "source": "http://github.com/maestrodev/puppet-test", 5 | "author": "maestrodev", 6 | "license": "Apache License, Version 2.0", 7 | "summary": "Testing Puppet module operations", 8 | "description": "Testing Puppet module operations", 9 | "project_page": "http://github.com/maestrodev/puppet-test", 10 | "dependencies": [ 11 | 12 | ], 13 | "types": [ 14 | 15 | ], 16 | "checksums": { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /features/push.feature: -------------------------------------------------------------------------------- 1 | Feature: push 2 | puppet-blacksmith needs to push modules to the Puppet Forge 3 | 4 | @skip 5 | Scenario: Pushing a module 6 | Given a file named "Rakefile" with: 7 | """ 8 | require "#{__dir__}/../../lib/puppet_blacksmith/rake_tasks" 9 | """ 10 | And a file named "metadata.json" with: 11 | """ 12 | { 13 | "name": "maestrodev-test", 14 | "version": "1.0.0", 15 | "author": "maestrodev", 16 | "license": "Apache-2.0", 17 | "project_page": "http://github.com/maestrodev/puppet-blacksmith", 18 | "source": "http://github.com/maestrodev/puppet-blacksmith", 19 | "summary": "Testing Puppet module operations", 20 | "description": "Testing Puppet module operations" 21 | } 22 | """ 23 | When I run `rake module:push` 24 | Then the exit status should be 0 25 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes 3 | 4 | changelog: 5 | exclude: 6 | labels: 7 | - duplicate 8 | - invalid 9 | - modulesync 10 | - question 11 | - skip-changelog 12 | - wont-fix 13 | - wontfix 14 | - github_actions 15 | 16 | categories: 17 | - title: Breaking Changes 🛠 18 | labels: 19 | - backwards-incompatible 20 | 21 | - title: New Features 🎉 22 | labels: 23 | - enhancement 24 | 25 | - title: Bug Fixes 🐛 26 | labels: 27 | - bug 28 | - bugfix 29 | 30 | - title: Documentation Updates 📚 31 | labels: 32 | - documentation 33 | - docs 34 | 35 | - title: Dependency Updates ⬆️ 36 | labels: 37 | - dependencies 38 | 39 | - title: Other Changes 40 | labels: 41 | - "*" 42 | -------------------------------------------------------------------------------- /spec/data/metadata-no-author.json: -------------------------------------------------------------------------------- 1 | { 2 | "operatingsystem_support": [ 3 | { 4 | "operatingsystem": "CentOS", 5 | "operatingsystemrelease": [ 6 | "4", 7 | "5", 8 | "6" 9 | ] 10 | } 11 | ], 12 | "requirements": [ 13 | { 14 | "name": "pe", 15 | "version_requirement": "3.2.x" 16 | }, 17 | { 18 | "name": "puppet", 19 | "version_requirement": ">=2.7.20 <7.0.0" 20 | } 21 | ], 22 | "name": "maestrodev-test", 23 | "version": "1.0.0", 24 | "source": "git://github.com/puppetlabs/puppetlabs-stdlib", 25 | "license": "Apache 2.0", 26 | "summary": "Puppet Module Standard Library", 27 | "description": "Standard Library for Puppet Modules", 28 | "project_page": "https://github.com/puppetlabs/puppetlabs-stdlib", 29 | "dependencies": [ 30 | { 31 | "name": "puppetlabs-stdlib", 32 | "version_requirement": ">= 3.0.0" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /spec/data/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "operatingsystem_support": [ 3 | { 4 | "operatingsystem": "CentOS", 5 | "operatingsystemrelease": [ 6 | "4", 7 | "5", 8 | "6" 9 | ] 10 | } 11 | ], 12 | "requirements": [ 13 | { 14 | "name": "pe", 15 | "version_requirement": "3.2.x" 16 | }, 17 | { 18 | "name": "puppet", 19 | "version_requirement": ">=2.7.20 <7.0.0" 20 | } 21 | ], 22 | "name": "maestrodev-test", 23 | "version": "1.0.0", 24 | "source": "git://github.com/puppetlabs/puppetlabs-stdlib", 25 | "author": "maestrodev", 26 | "license": "Apache 2.0", 27 | "summary": "Puppet Module Standard Library", 28 | "description": "Standard Library for Puppet Modules", 29 | "project_page": "https://github.com/puppetlabs/puppetlabs-stdlib", 30 | "dependencies": [ 31 | { 32 | "name": "puppetlabs-stdlib", 33 | "version_requirement": ">= 3.0.0" 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /spec/data/metadata-different-author.json: -------------------------------------------------------------------------------- 1 | { 2 | "operatingsystem_support": [ 3 | { 4 | "operatingsystem": "CentOS", 5 | "operatingsystemrelease": [ 6 | "4", 7 | "5", 8 | "6" 9 | ] 10 | } 11 | ], 12 | "requirements": [ 13 | { 14 | "name": "pe", 15 | "version_requirement": "3.2.x" 16 | }, 17 | { 18 | "name": "puppet", 19 | "version_requirement": ">=2.7.20 <7.0.0" 20 | } 21 | ], 22 | "name": "maestrodev-test", 23 | "version": "1.0.0", 24 | "source": "git://github.com/puppetlabs/puppetlabs-stdlib", 25 | "author": "MaestroDev", 26 | "license": "Apache 2.0", 27 | "summary": "Puppet Module Standard Library", 28 | "description": "Standard Library for Puppet Modules", 29 | "project_page": "https://github.com/puppetlabs/puppetlabs-stdlib", 30 | "dependencies": [ 31 | { 32 | "name": "puppetlabs-stdlib", 33 | "version_requirement": ">= 3.0.0" 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /puppet-blacksmith.gemspec: -------------------------------------------------------------------------------- 1 | lib = File.expand_path('lib', __dir__) 2 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 3 | require 'puppet_blacksmith/version' 4 | 5 | Gem::Specification.new do |s| 6 | s.name = 'puppet-blacksmith' 7 | s.version = Blacksmith::VERSION 8 | 9 | s.authors = ['MaestroDev', 'Vox Pupuli'] 10 | s.summary = 'Tasks to manage Puppet module builds' 11 | s.description = 'Puppet module tools for development and Puppet Forge management' 12 | s.email = ['voxpupuli@groups.io'] 13 | s.homepage = 'http://github.com/voxpupuli/puppet-blacksmith' 14 | s.license = 'Apache-2.0' 15 | s.require_paths = ['lib'] 16 | 17 | s.required_ruby_version = '>= 3.2.0', '< 4' 18 | 19 | s.add_dependency 'base64', '>= 0.2', '< 0.4' 20 | s.add_dependency 'puppet-modulebuilder', '~> 2.0', '>= 2.0.2' 21 | s.add_dependency 'rest-client', '~>2.0' 22 | s.add_development_dependency 'aruba', '~> 2.1' 23 | s.add_development_dependency 'cucumber', '>= 9', '< 11' 24 | s.add_development_dependency 'rake', '~> 13.0', '>= 13.0.6' 25 | s.add_development_dependency 'rspec', '~> 3.12' 26 | s.add_development_dependency 'voxpupuli-rubocop', '~> 5.0.0' 27 | s.add_development_dependency 'webmock', '>= 2.0', '< 4' 28 | 29 | s.files = Dir.glob('lib/**/*') + %w[LICENSE] 30 | end 31 | -------------------------------------------------------------------------------- /spec/puppet_blacksmith/forge_shared.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'fileutils' 3 | 4 | RSpec.shared_examples 'forge_push' do |_collection_class| 5 | it 'pushes the module' do 6 | subject.push!(module_name, package) 7 | end 8 | end 9 | 10 | RSpec.shared_context 'forge' do 11 | subject { Blacksmith::Forge.new(username, password, forge) } 12 | 13 | let(:api_key) { 'e52f78b62e97cb8d8db6659a73aa522cca0f5c74d4714e0ed0bdd10000000000' } 14 | let(:username) { 'johndoe' } 15 | let(:password) { 'secret' } 16 | let(:forge) { 'https://forgestagingapi.puppetlabs.com' } 17 | let(:version) { '1.0.0' } 18 | let(:module_name) { 'maestrodev-test' } 19 | let(:spec_data) { File.join(__dir__, '..', 'data') } 20 | let(:spec_module) { File.join(spec_data, module_name) } 21 | let(:target) { File.expand_path(File.join(__dir__, '..', '..', 'pkg', module_name)) } 22 | let(:package) { "#{target}.tar.gz" } 23 | 24 | let(:headers) { { 'User-Agent' => %r{^Blacksmith/#{Blacksmith::VERSION} Ruby/.* \(.*\)$}o } } 25 | 26 | def create_tarball 27 | FileUtils.mkdir_p(target) 28 | 29 | # update version 30 | f = File.join(spec_module, 'metadata.json') 31 | metadata = JSON.parse File.read(f) 32 | metadata['version'] = "1.0.#{Random.rand(9_999_999)}" 33 | File.write(File.join(target, 'metadata.json'), JSON.pretty_generate(metadata)) 34 | `cd '#{target}/..'; tar -czf #{module_name}.tar.gz #{module_name}` 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /features/module.feature: -------------------------------------------------------------------------------- 1 | Feature: module 2 | puppet-blacksmith needs to build a module 3 | 4 | Scenario: Building a module with metadata.json 5 | Given a file named "Rakefile" with: 6 | """ 7 | require "#{__dir__}/../../lib/puppet_blacksmith/rake_tasks" 8 | """ 9 | And a file named "metadata.json" with: 10 | """ 11 | { 12 | "operatingsystem_support": [ 13 | { 14 | "operatingsystem": "CentOS", 15 | "operatingsystemrelease": [ 16 | "4", 17 | "5", 18 | "6" 19 | ] 20 | } 21 | ], 22 | "requirements": [ 23 | { 24 | "name": "pe", 25 | "version_requirement": "3.2.x" 26 | }, 27 | { 28 | "name": "puppet", 29 | "version_requirement": ">=2.7.20 <7.0.0" 30 | } 31 | ], 32 | "name": "maestrodev-test", 33 | "version": "1.0.0", 34 | "source": "git://github.com/puppetlabs/puppetlabs-stdlib", 35 | "author": "maestrodev", 36 | "license": "Apache 2.0", 37 | "summary": "Puppet Module Standard Library", 38 | "description": "Standard Library for Puppet Modules", 39 | "project_page": "https://github.com/puppetlabs/puppetlabs-stdlib", 40 | "dependencies": [ 41 | ] 42 | } 43 | """ 44 | When I run `rake module:build` 45 | Then the exit status should be 0 46 | And a file named "pkg/maestrodev-test-1.0.0.tar.gz" should exist 47 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'rake/clean' 3 | require 'rubygems' 4 | require 'bundler/gem_tasks' 5 | require 'rspec/core/rake_task' 6 | require 'cucumber' 7 | require 'cucumber/rake/task' 8 | require 'fileutils' 9 | 10 | CLEAN.include('pkg/', 'tmp/') 11 | CLOBBER.include('Gemfile.lock') 12 | 13 | $LOAD_PATH.unshift(File.expand_path('lib', __dir__)) 14 | require 'puppet_blacksmith/version' 15 | 16 | task default: %i[clean spec cucumber build] 17 | 18 | RSpec::Core::RakeTask.new(:spec) do |t| 19 | t.rspec_opts = '--tag ~live' 20 | end 21 | 22 | Cucumber::Rake::Task.new(:cucumber) do |t| 23 | end 24 | 25 | desc 'bump the version' 26 | task :bump do 27 | v = Gem::Version.new("#{Blacksmith::VERSION}.0") 28 | raise("Unable to increase prerelease version #{Blacksmith::VERSION}") if v.prerelease? 29 | 30 | s = <<~EOS 31 | module Blacksmith 32 | VERSION = #{v.bump} 33 | end 34 | EOS 35 | 36 | File.open('lib/puppet_blacksmith/version.rb', 'w') do |file| 37 | file.print s 38 | end 39 | sh 'git add version' 40 | sh "git commit -m 'Bump version'" 41 | end 42 | 43 | begin 44 | require 'rubygems' 45 | require 'github_changelog_generator/task' 46 | rescue LoadError 47 | # github_changelog_generator is an optional group 48 | else 49 | GitHubChangelogGenerator::RakeTask.new :changelog do |config| 50 | config.exclude_labels = %w[duplicate question invalid wontfix wont-fix skip-changelog github_actions] 51 | config.user = 'voxpupuli' 52 | config.project = 'puppet-blacksmith' 53 | gem_version = Gem::Specification.load("#{config.project}.gemspec").version 54 | config.future_release = gem_version 55 | end 56 | end 57 | 58 | begin 59 | require 'voxpupuli/rubocop/rake' 60 | rescue LoadError 61 | # the voxpupuli-rubocop gem is optional 62 | end 63 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Test 3 | 4 | on: 5 | pull_request: {} 6 | push: 7 | branches: 8 | - master 9 | 10 | # minimal permissions 11 | permissions: 12 | contents: read 13 | 14 | jobs: 15 | rubocop_and_matrix: 16 | runs-on: ubuntu-24.04 17 | outputs: 18 | ruby: ${{ steps.ruby.outputs.versions }} 19 | steps: 20 | - uses: actions/checkout@v6 21 | - name: Install Ruby ${{ matrix.ruby }} 22 | uses: ruby/setup-ruby@v1 23 | with: 24 | ruby-version: "3.4" 25 | bundler-cache: true 26 | - name: Run Rubocop 27 | run: bundle exec rake rubocop 28 | - id: ruby 29 | uses: voxpupuli/ruby-version@v1 30 | 31 | rspec: 32 | runs-on: ubuntu-24.04 33 | needs: rubocop_and_matrix 34 | strategy: 35 | fail-fast: false 36 | matrix: 37 | ruby: ${{ fromJSON(needs.rubocop_and_matrix.outputs.ruby) }} 38 | name: RSpec - Ruby ${{ matrix.ruby }} 39 | steps: 40 | - uses: actions/checkout@v6 41 | - name: Install Ruby ${{ matrix.ruby }} 42 | uses: ruby/setup-ruby@v1 43 | with: 44 | ruby-version: ${{ matrix.ruby }} 45 | bundler-cache: true 46 | - name: set git config 47 | run: git config --global init.defaultBranch main && git config --global user.email 'user@example.com' && git config --global user.name 'user' 48 | - name: spec tests 49 | env: 50 | GIT_COMMITTER_NAME: 'user' 51 | GIT_COMMITTER_EMAIL: 'user@example.com' 52 | GIT_AUTHOR_NAME: 'user' 53 | GIT_AUTHOR_EMAIL: 'user@example.com' 54 | run: bundle exec rake 55 | - name: Verify gem builds 56 | run: gem build --strict --verbose *.gemspec 57 | 58 | tests: 59 | needs: 60 | - rubocop_and_matrix 61 | - rspec 62 | runs-on: ubuntu-24.04 63 | name: Test suite 64 | steps: 65 | - run: echo Test suite completed 66 | -------------------------------------------------------------------------------- /lib/puppet_blacksmith/modulefile.rb: -------------------------------------------------------------------------------- 1 | require 'puppet_blacksmith/version_helper' 2 | 3 | module Blacksmith 4 | class Modulefile 5 | FILES = ['metadata.json'] 6 | 7 | attr_reader :path 8 | 9 | def initialize(path = nil) 10 | @path = path.nil? ? FILES.find { |f| File.exist? f } : path 11 | raise Blacksmith::Error, "Unable to find any of #{FILES}" unless @path 12 | end 13 | 14 | def metadata 15 | @metadata ||= JSON.parse(File.read(path)) 16 | @metadata 17 | end 18 | 19 | # name in metadata.json is author-modulename 20 | def name 21 | metadata['name'].split('-', 2)[1] 22 | end 23 | 24 | def namespace 25 | metadata['name'].split('-', 2)[0] 26 | end 27 | 28 | def author 29 | metadata['author'] || namespace 30 | end 31 | 32 | def version 33 | metadata['version'] 34 | end 35 | 36 | def bump_to_version!(new_version) 37 | text = File.read(path) 38 | text = replace_version(text, new_version) 39 | File.open(path, 'w') { |file| file.puts text } 40 | new_version 41 | end 42 | 43 | def bump!(level = :patch) 44 | new_version = increase_version(version, level) 45 | bump_to_version!(new_version) 46 | end 47 | 48 | %i[major minor patch full].each do |level| 49 | define_method("bump_#{level}!") { bump!(level) } 50 | end 51 | 52 | def bump_dep!(module_name, version) 53 | text = File.read(path) 54 | text = replace_dependency_version(text, module_name, version) 55 | File.open(path, 'w') { |file| file.puts text } 56 | end 57 | 58 | def replace_version(text, version) 59 | json = JSON.parse(text) 60 | json['version'] = version 61 | JSON.pretty_generate(json) 62 | end 63 | 64 | def increase_version(version, level = :patch) 65 | v = VersionHelper::Version.new(version) 66 | v.send("#{level}!").to_s 67 | end 68 | 69 | def replace_dependency_version(text, module_name, version) 70 | module_name = module_name.sub(%r{/}, '-') 71 | json = JSON.parse(text) 72 | new_dep_list = [] 73 | json['dependencies'].each do |dep| 74 | dep['version_requirement'] = version if dep['name'] == module_name 75 | new_dep_list << dep 76 | end 77 | json['dependencies'] = new_dep_list 78 | JSON.pretty_generate(json) 79 | end 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /lib/puppet_blacksmith/git.rb: -------------------------------------------------------------------------------- 1 | require 'open3' 2 | 3 | module Blacksmith 4 | class Git 5 | attr_accessor :path, :tag_message_pattern, :tag_sign 6 | attr_writer :tag_pattern, :commit_message_pattern 7 | 8 | # Pattern to use for tags, %s is replaced with the actual version 9 | def commit_message_pattern 10 | @commit_message_pattern || '[blacksmith] Bump version to %s' 11 | end 12 | 13 | def tag_pattern 14 | @tag_pattern || 'v%s' 15 | end 16 | 17 | def initialize(path = '.') 18 | @path = File.expand_path(path) 19 | end 20 | 21 | def has_tag?(tag) 22 | exec_git(['tag', '--list', tag]).strip == tag 23 | end 24 | 25 | def has_version_tag?(version) 26 | tag = tag_pattern % version 27 | has_tag? tag 28 | end 29 | 30 | def tag!(version) 31 | tag = tag_pattern % version 32 | command = ['tag', tag] 33 | if tag_message_pattern 34 | tag_message = tag_message_pattern % version 35 | command += ['-m', tag_message] 36 | end 37 | if tag_sign 38 | raise Blacksmith::Error, 'Signed tags require messages - set tag_message_pattern' unless tag_message_pattern 39 | 40 | command += ['-s'] 41 | end 42 | exec_git command 43 | end 44 | 45 | def commit_modulefile!(version) 46 | files = Blacksmith::Modulefile::FILES.select { |f| File.exist?(File.join(@path, f)) } 47 | message = commit_message_pattern % version 48 | s = exec_git ['add'] + files 49 | s += exec_git ['commit', '-m', message] 50 | s 51 | end 52 | 53 | def push! 54 | s = exec_git ['push'] 55 | s += exec_git ['push', '--tags'] 56 | s 57 | end 58 | 59 | private 60 | 61 | def exec_git(cmd) 62 | out = '' 63 | err = '' 64 | exit_status = nil 65 | new_cmd = ['git', '--git-dir', File.join(@path, '.git'), '--work-tree', @path] + cmd 66 | # wait_thr is nil in JRuby < 1.7.5 see http://jira.codehaus.org/browse/JRUBY-6409 67 | Open3.popen3(*new_cmd) do |_stdin, stdout, stderr, wait_thr| 68 | out = stdout.read 69 | err = stderr.read 70 | exit_status = wait_thr.nil? ? nil : wait_thr.value 71 | end 72 | if exit_status.nil? 73 | unless err.empty? 74 | raise Blacksmith::Error, 75 | "Command #{new_cmd} failed with stderr:\n#{err}#{"\nstdout:\n" + out unless out.empty?}" 76 | end 77 | elsif !exit_status.success? 78 | msg = err.empty? ? out : err 79 | msg = "\n#{msg}" unless msg.empty? 80 | raise Blacksmith::Error, "Command #{new_cmd} failed with exit status #{exit_status}#{msg}" 81 | end 82 | out 83 | end 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /features/update_dependency.feature: -------------------------------------------------------------------------------- 1 | Feature: update_dependency_version 2 | puppet-blacksmith needs to update module dependency versions 3 | 4 | Scenario: Bumping a module dependency version when using metadata.json 5 | Given a file named "Rakefile" with: 6 | """ 7 | require "#{__dir__}/../../lib/puppet_blacksmith/rake_tasks" 8 | """ 9 | And a file named "metadata.json" with: 10 | """ 11 | { 12 | "operatingsystem_support": [ 13 | { 14 | "operatingsystem": "CentOS", 15 | "operatingsystemrelease": [ 16 | "4", 17 | "5", 18 | "6" 19 | ] 20 | } 21 | ], 22 | "requirements": [ 23 | { 24 | "name": "pe", 25 | "version_requirement": "3.2.x" 26 | }, 27 | { 28 | "name": "puppet", 29 | "version_requirement": ">=2.7.20 <7.0.0" 30 | } 31 | ], 32 | "name": "maestrodev-test", 33 | "version": "1.0.0", 34 | "source": "git://github.com/puppetlabs/puppetlabs-stdlib", 35 | "author": "maestrodev", 36 | "license": "Apache 2.0", 37 | "summary": "Puppet Module Standard Library", 38 | "description": "Standard Library for Puppet Modules", 39 | "project_page": "https://github.com/puppetlabs/puppetlabs-stdlib", 40 | "dependencies": [ 41 | { 42 | "name": "puppetlabs-stdlib", 43 | "version_requirement": ">= 3.0.0" 44 | } 45 | ] 46 | } 47 | """ 48 | When I run `rake module:dependency["puppetlabs-stdlib",">= 4.0.0"]` 49 | Then the exit status should be 0 50 | And the file "metadata.json" should contain: 51 | """ 52 | { 53 | "operatingsystem_support": [ 54 | { 55 | "operatingsystem": "CentOS", 56 | "operatingsystemrelease": [ 57 | "4", 58 | "5", 59 | "6" 60 | ] 61 | } 62 | ], 63 | "requirements": [ 64 | { 65 | "name": "pe", 66 | "version_requirement": "3.2.x" 67 | }, 68 | { 69 | "name": "puppet", 70 | "version_requirement": ">=2.7.20 <7.0.0" 71 | } 72 | ], 73 | "name": "maestrodev-test", 74 | "version": "1.0.0", 75 | "source": "git://github.com/puppetlabs/puppetlabs-stdlib", 76 | "author": "maestrodev", 77 | "license": "Apache 2.0", 78 | "summary": "Puppet Module Standard Library", 79 | "description": "Standard Library for Puppet Modules", 80 | "project_page": "https://github.com/puppetlabs/puppetlabs-stdlib", 81 | "dependencies": [ 82 | { 83 | "name": "puppetlabs-stdlib", 84 | "version_requirement": ">= 4.0.0" 85 | } 86 | ] 87 | """ 88 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Gem Release 3 | 4 | on: 5 | push: 6 | tags: 7 | - '*' 8 | 9 | permissions: {} 10 | 11 | jobs: 12 | build-release: 13 | # Prevent releases from forked repositories 14 | if: github.repository_owner == 'voxpupuli' 15 | name: Build the gem 16 | runs-on: ubuntu-24.04 17 | steps: 18 | - uses: actions/checkout@v6 19 | - name: Install Ruby 20 | uses: ruby/setup-ruby@v1 21 | with: 22 | ruby-version: 'ruby' 23 | - name: Build gem 24 | shell: bash 25 | run: gem build --verbose *.gemspec 26 | - name: Upload gem to GitHub cache 27 | uses: actions/upload-artifact@v6 28 | with: 29 | name: gem-artifact 30 | path: '*.gem' 31 | retention-days: 1 32 | compression-level: 0 33 | 34 | create-github-release: 35 | needs: build-release 36 | name: Create GitHub release 37 | runs-on: ubuntu-24.04 38 | permissions: 39 | contents: write # clone repo and create release 40 | steps: 41 | - name: Download gem from GitHub cache 42 | uses: actions/download-artifact@v7 43 | with: 44 | name: gem-artifact 45 | - name: Create Release 46 | shell: bash 47 | env: 48 | GH_TOKEN: ${{ github.token }} 49 | run: gh release create --repo ${{ github.repository }} ${{ github.ref_name }} --generate-notes *.gem 50 | 51 | release-to-github: 52 | needs: build-release 53 | name: Release to GitHub 54 | runs-on: ubuntu-24.04 55 | permissions: 56 | packages: write # publish to rubygems.pkg.github.com 57 | steps: 58 | - name: Download gem from GitHub cache 59 | uses: actions/download-artifact@v7 60 | with: 61 | name: gem-artifact 62 | - name: Publish gem to GitHub packages 63 | run: gem push --host https://rubygems.pkg.github.com/${{ github.repository_owner }} *.gem 64 | env: 65 | GEM_HOST_API_KEY: ${{ secrets.GITHUB_TOKEN }} 66 | 67 | release-to-rubygems: 68 | needs: build-release 69 | name: Release gem to rubygems.org 70 | runs-on: ubuntu-24.04 71 | environment: release # recommended by rubygems.org 72 | permissions: 73 | id-token: write # rubygems.org authentication 74 | steps: 75 | - name: Download gem from GitHub cache 76 | uses: actions/download-artifact@v7 77 | with: 78 | name: gem-artifact 79 | - uses: rubygems/configure-rubygems-credentials@v1.0.0 80 | - name: Publish gem to rubygems.org 81 | shell: bash 82 | run: gem push *.gem 83 | 84 | release-verification: 85 | name: Check that all releases are done 86 | runs-on: ubuntu-24.04 87 | permissions: 88 | contents: read # minimal permissions that we have to grant 89 | needs: 90 | - create-github-release 91 | - release-to-github 92 | - release-to-rubygems 93 | steps: 94 | - name: Download gem from GitHub cache 95 | uses: actions/download-artifact@v7 96 | with: 97 | name: gem-artifact 98 | - name: Install Ruby 99 | uses: ruby/setup-ruby@v1 100 | with: 101 | ruby-version: 'ruby' 102 | - name: Wait for release to propagate 103 | shell: bash 104 | run: | 105 | gem install rubygems-await 106 | gem await *.gem 107 | -------------------------------------------------------------------------------- /spec/puppet_blacksmith/forge_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require_relative 'forge_shared' 3 | require 'fileutils' 4 | require 'webmock/rspec' 5 | 6 | describe 'Blacksmith::Forge' do 7 | include_context 'forge' 8 | 9 | describe 'resolving credentials' do 10 | before do 11 | allow(Dir).to receive(:pwd).and_return('/home/mr_puppet/puppet-some-module') 12 | allow(File).to receive(:expand_path).with('~/.puppetforge.yml').and_return('/home/mr_puppet/.puppetforge.yml') 13 | end 14 | 15 | it 'prefers env vars to file values' do 16 | stubbed_forge_password = 'asdf1234' 17 | 18 | allow(File).to receive(:exist?) 19 | .with('/home/mr_puppet/puppet-some-module/.puppetforge.yml') 20 | .and_return(false) 21 | allow(File).to receive(:exist?) 22 | .with('/home/mr_puppet/.puppetforge.yml') 23 | .and_return(true) 24 | allow(YAML).to receive(:load_file) 25 | .with('/home/mr_puppet/.puppetforge.yml') 26 | .and_return({ 'username' => username, 27 | 'password' => password, }) 28 | allow(ENV).to receive(:[]) 29 | .with(any_args) 30 | allow(ENV).to receive(:[]) 31 | .with('BLACKSMITH_FORGE_PASSWORD') 32 | .and_return(stubbed_forge_password) 33 | 34 | forge = Blacksmith::Forge.new 35 | 36 | expect(forge.url).to eq(Blacksmith::Forge::PUPPETLABS_FORGE) 37 | expect(forge.password).to eq(stubbed_forge_password) 38 | expect(forge.username).to eq(username) 39 | end 40 | 41 | context 'when the credentials values are unset' do 42 | it 'raises an error' do 43 | expect do 44 | Blacksmith::Forge.new(nil, password, forge) 45 | end.to raise_error(/Could not find Puppet Forge credentials/) 46 | end 47 | end 48 | 49 | it 'loads credentials from home dir' do 50 | allow(File).to receive(:exist?).with('/home/mr_puppet/puppet-some-module/.puppetforge.yml').and_return(false) 51 | allow(File).to receive(:exist?).with('/home/mr_puppet/.puppetforge.yml').and_return(true) 52 | allow(YAML).to receive(:load_file).with('/home/mr_puppet/.puppetforge.yml').and_return({ 'username' => 'puppet-user' }) 53 | 54 | subject = Blacksmith::Forge.new(nil, password, forge) 55 | expect(subject.username).to eq('puppet-user') 56 | end 57 | 58 | it 'loads credentials from project dir' do 59 | allow(File).to receive(:exist?).with('/home/mr_puppet/puppet-some-module/.puppetforge.yml').and_return(true) 60 | allow(File).to receive(:exist?).with('/home/mr_puppet/.puppetforge.yml').and_return(true) 61 | allow(YAML).to receive(:load_file).with('/home/mr_puppet/puppet-some-module/.puppetforge.yml').and_return({ 'username' => 'puppet-other-user' }) 62 | 63 | subject = Blacksmith::Forge.new(nil, password, forge) 64 | expect(subject.username).to eq('puppet-other-user') 65 | end 66 | end 67 | 68 | describe 'push' do 69 | before { create_tarball } 70 | 71 | context 'when using a Forge API key' do 72 | before do 73 | stub_request(:post, "#{forge}/v3/releases").with( 74 | headers: headers.merge({ 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 75 | 'Authorization' => 'Bearer e52f78b62e97cb8d8db6659a73aa522cca0f5c74d4714e0ed0bdd10000000000', 'Content-Type' => %r{\Amultipart/form-data;}, }), 76 | ) do |request| 77 | request.body =~ %r{Content-Disposition: form-data; name="file"; filename="maestrodev-test.tar.gz"\r\nContent-Type: application/gzip} 78 | end.to_return(status: 200, body: File.read(File.join(spec_data, 'response.json')), headers: {}) 79 | end 80 | 81 | it 'push the module' do 82 | subject.api_key = api_key 83 | subject.url = forge 84 | 85 | resp = subject.push!(module_name, package) 86 | expect(resp.code).to eq(200) 87 | end 88 | end 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /features/git.feature: -------------------------------------------------------------------------------- 1 | Feature: git 2 | puppet-blacksmith needs to commit and tag git sources 3 | 4 | Scenario: Tagging and commiting 5 | Given I run `git clone https://github.com/maestrodev/puppet-test.git .` 6 | And I run `git config user.email "user@example.com"` 7 | And I run `git checkout -b test v1.0.0` 8 | When I run `git tag` 9 | Then the output should not match /^v1\.0\.1$/ 10 | Given a file named "Rakefile" with: 11 | """ 12 | require "#{__dir__}/../../lib/puppet_blacksmith/rake_tasks" 13 | """ 14 | And a file named "metadata.json" with: 15 | """ 16 | { 17 | "name": "maestrodev-test", 18 | "version": "1.0.1", 19 | "dependencies": [] 20 | } 21 | """ 22 | When I run `rake module:tag module:bump_commit` 23 | Then the output should not contain "rake aborted!" 24 | Then the exit status should be 0 25 | And the file "metadata.json" should contain: 26 | """ 27 | { 28 | "name": "maestrodev-test", 29 | "version": "1.0.2", 30 | "dependencies": [] 31 | } 32 | """ 33 | When I run `git tag` 34 | Then the output should match /^v1\.0\.1$/ 35 | When I run `git show --format=%s` 36 | Then the output should match /^\[blacksmith\] Bump version to 1\.0\.2$/ 37 | When I run `git describe` 38 | Then the output should match /^fatal: No annotated tags can describe/ 39 | 40 | Scenario: Tagging and commiting in a path with spaces 41 | Given a directory named "path with spaces" 42 | When I cd to "path with spaces" 43 | Given I run `git clone https://github.com/maestrodev/puppet-test.git .` 44 | And I run `git config user.email "user@example.com"` 45 | And I run `git checkout -b test v1.0.0` 46 | When I run `git tag` 47 | Then the output should not match /^v1\.0\.1$/ 48 | Given a file named "Rakefile" with: 49 | """ 50 | require "#{__dir__}/../../../lib/puppet_blacksmith/rake_tasks" 51 | """ 52 | And a file named "metadata.json" with: 53 | """ 54 | { 55 | "name": "maestrodev-test", 56 | "version": "1.0.1", 57 | "dependencies": [] 58 | } 59 | """ 60 | When I run `rake module:tag module:bump_commit` 61 | Then the output should not contain "rake aborted!" 62 | Then the exit status should be 0 63 | And the file "metadata.json" should contain: 64 | """ 65 | { 66 | "name": "maestrodev-test", 67 | "version": "1.0.2", 68 | "dependencies": [] 69 | } 70 | """ 71 | When I run `git tag` 72 | Then the output should match /^v1\.0\.1$/ 73 | When I run `git show --format=%s` 74 | Then the output should match /^\[blacksmith\] Bump version to 1\.0\.2$/ 75 | 76 | Scenario: Tagging and commiting with custom patterns 77 | Given I run `git clone https://github.com/maestrodev/puppet-test.git .` 78 | And I run `git config user.email "user@example.com"` 79 | And I run `git checkout -b test v1.0.0` 80 | When I run `git tag` 81 | Then the output should not match /^1\.0\.1$/ 82 | Given a file named "Rakefile" with: 83 | """ 84 | require "#{__dir__}/../../lib/puppet_blacksmith/rake_tasks" 85 | Blacksmith::RakeTask.new do |t| 86 | t.tag_pattern = "%s" 87 | t.tag_message_pattern = "Version %s" 88 | t.commit_message_pattern = "New version %s" 89 | end 90 | """ 91 | And a file named "metadata.json" with: 92 | """ 93 | { 94 | "name": "maestrodev-test", 95 | "version": "1.0.1", 96 | "dependencies": [] 97 | } 98 | """ 99 | When I run `rake module:tag module:bump_commit` 100 | Then the output should not contain "rake aborted!" 101 | Then the exit status should be 0 102 | And the file "metadata.json" should contain: 103 | """ 104 | { 105 | "name": "maestrodev-test", 106 | "version": "1.0.2", 107 | "dependencies": [] 108 | } 109 | """ 110 | When I run `git tag` 111 | Then the output should match /^1\.0\.1$/ 112 | And the output should not match /^v1\.0\.1$/ 113 | When I run `git show --format=%s` 114 | Then the output should match /^New version 1\.0\.2$/ 115 | When I run `git describe` 116 | Then the output should match /^1\.0\.1$/ 117 | -------------------------------------------------------------------------------- /spec/puppet_blacksmith/git_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'Blacksmith::Git' do 4 | subject { Blacksmith::Git.new(path) } 5 | 6 | let(:path) { File.expand_path(File.join(__dir__, '../../tmp/git_test')) } 7 | let(:version) { '1.0.0' } 8 | let(:metadata_file) { 'metadata.json' } 9 | 10 | before do 11 | FileUtils.rm_rf path 12 | FileUtils.mkdir_p(path) 13 | `git init '#{path}'` 14 | FileUtils.touch(File.join(path, metadata_file)) 15 | `cd '#{path}' && git add #{metadata_file} && git commit -am "Init"` 16 | end 17 | 18 | shared_examples_for 'git' do 19 | describe 'has_tag?' do 20 | context 'with a tag' do 21 | before { subject.tag!(version) } 22 | 23 | it { expect(subject.has_tag?("v#{version}")).to be true } 24 | end 25 | 26 | context 'with a partial match' do 27 | before { subject.tag!(version) } 28 | 29 | it { expect(subject.has_tag?(version)).to be false } 30 | end 31 | 32 | context 'without a tag' do 33 | it { expect(subject.has_tag?('something')).to be false } 34 | end 35 | end 36 | 37 | describe 'has_tag?' do 38 | context 'with a tag' do 39 | before { subject.tag!(version) } 40 | 41 | it { expect(subject.has_version_tag?(version)).to be true } 42 | end 43 | 44 | context 'without a tag' do 45 | it { expect(subject.has_version_tag?('something')).to be false } 46 | end 47 | end 48 | 49 | describe 'tag!' do 50 | context 'basic tag' do 51 | before { subject.tag!(version) } 52 | 53 | it 'has the tag' do 54 | out = `cd '#{path}' && git tag` 55 | expect(out.chomp).to match(/^v1.0.0$/) 56 | end 57 | end 58 | 59 | context 'signed tag without pattern' do 60 | before do 61 | subject.tag_message_pattern = nil 62 | subject.tag_sign = true 63 | end 64 | 65 | it { expect { subject.tag!(version) }.to raise_error(Blacksmith::Error, /Signed tags require messages/) } 66 | end 67 | 68 | context 'signed tag with pattern' do 69 | before do 70 | subject.tag_message_pattern = 'Version %s' 71 | subject.tag_sign = true 72 | allow(subject).to receive(:exec_git).with(['tag', 'v1.0.0', '-m', 'Version 1.0.0', '-s']).and_return(true) 73 | end 74 | 75 | it { expect(subject.tag!(version)).to be true } 76 | end 77 | end 78 | 79 | describe 'commit_modulefile' do 80 | before do 81 | open(File.join(subject.path, metadata_file), 'a') do |f| 82 | f.puts 'more text' 83 | end 84 | end 85 | 86 | it 'commits the metadata file' do 87 | expect(subject.commit_modulefile!(version)).to match(/\[blacksmith\] Bump version to #{version}/) 88 | end 89 | end 90 | 91 | describe 'exec_git' do 92 | let(:cmd) { ['log'] } 93 | let(:stdin) { nil } 94 | let(:stdout) { '' } 95 | let(:stderr) { '' } 96 | let(:exit_code) { double('exit_code', success?: true) } 97 | let(:wait_thr) { double('wait_thr', value: exit_code) } 98 | 99 | context 'when git succeeds' do 100 | before do 101 | allow(Open3).to receive(:popen3) 102 | .with('git', '--git-dir', File.join(path, '.git'), '--work-tree', path, *cmd) 103 | .and_yield(nil, double(read: stdout), double(read: stderr), wait_thr) 104 | expect { subject.send(:exec_git, cmd) }.not_to raise_error 105 | end 106 | 107 | context 'when stderr is empty' do 108 | it {} 109 | end 110 | 111 | context 'when stderr is not empty' do 112 | let(:stderr) { 'some error' } 113 | 114 | it {} 115 | end 116 | end 117 | 118 | context 'when git fails' do 119 | # this spec fails on jruby, can't detect exit code of script 120 | context 'when stderr is empty' do 121 | let(:cmd) { [] } # exits with 1 122 | 123 | it { 124 | expect do 125 | subject.send(:exec_git, cmd) 126 | end.to raise_error(Blacksmith::Error, /^Command .* failed with exit status.*1.*$/) 127 | } 128 | end 129 | 130 | context 'when stderr is not empty' do 131 | let(:cmd) { %w[help xxxx] } # exits with 1 and prints to stdout 132 | 133 | it { expect { subject.send(:exec_git, cmd) }.to raise_error(Blacksmith::Error, /No manual entry for gitxxx/) } 134 | end 135 | end 136 | end 137 | end 138 | 139 | context 'Using metadata.json' do 140 | it_behaves_like 'git' 141 | end 142 | end 143 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | ## [v6.0.1](https://github.com/voxpupuli/puppet-blacksmith/tree/v6.0.1) (2020-10-06) 2 | 3 | * Add support for Puppet Forge API keys 4 | * Add the full license text 5 | * Require puppet-modulebuilder ~> 0.2 6 | 7 | ## 6.0.1 8 | 9 | * Drop old ruby code 10 | * We had some code for Ruby 2.0 in the Gemfile. Since we require Ruby 2.4, we can drop the old code 11 | * Forge upload should always use namespace 12 | 13 | There is an author field in metadata.json, but `pdk build` always uses 14 | the module's namespace when building the tarball. Therefore, Blacksmith 15 | should do the same and not even use the author field. 16 | 17 | ## 6.0.0 18 | 19 | This module moves from [puppetlabs_spec_helper](https://github.com/puppetlabs/puppetlabs_spec_helper)'s `build` rake task to its own `module:build` task which uses [puppet-modulebuilder](https://github.com/puppetlabs/puppet-modulebuilder). This has the benefit that it no longer needs the Puppet face (Puppet < 6) or the PDK (Puppet >= 6) and becomes standalone. 20 | 21 | * Use puppet-modulebuilder to build modules 22 | 23 | ## 5.1.0 24 | 25 | * Enable Artifactory access via API key. This provides an additional authentication mechanism besides a password 26 | * Remove outdated variable reference. This fixes a regression in Artifactory upload exception handling 27 | 28 | ## 5.0.0 29 | 30 | Note: The gemspec got updated to require at least Ruby 2.4 or newer. Older 31 | versions are end of life and unsupported. 32 | 33 | * Add note about clearing the rake task cache 34 | * Add support for Artifactory uploads 35 | * Update the maximum allows puppet versions 36 | * Update description for module:release task 37 | * Factor out uploading to separate functions 38 | * Handle RC versions while patching 39 | * Improve testing code 40 | 41 | ## 4.1.2 42 | 43 | * Fix an incorrect checksum on rubygems 44 | 45 | ## 4.1.1 46 | 47 | * Expose `tag_sign` option in the RakeTask 48 | 49 | ## 4.1.0 50 | 51 | * Allow creating signed tags 52 | 53 | ## 4.0.1 54 | 55 | This version is a fixup release since the 4.0.0 release broke the CI of a user which was still using Ruby 1.9.3. 56 | 57 | * Require ruby >= 2.0.0 in the gemspec 58 | 59 | ## 4.0.0 60 | 61 | This version drops Modulefile support. Upstream Puppet has removed the support from the code and we used that. It means we no longer use the Puppet gem which greatly reduces the profile of the module. 62 | 63 | * Add has_tag? and has_version_tag? methods 64 | * Make exec_git a private method 65 | * Support Ruby 2.2 - 2.4 66 | * Rip out Modulefile support 67 | * Allow using newer rest-client and webmock versions 68 | * Handle spaces in git paths 69 | * Allow creating annotated git commits 70 | * Make commit message configurable 71 | * Fix rake module release order 72 | * Drop ruby 1.9 support 73 | 74 | ## 3.4.0 75 | 76 | * Pin rest-client and webmock for ruby 1.9 support 77 | * Add 'version', 'version:next' and 'version:next:[patch|major|minor]' 78 | * Allow loading forge credentials from project dir 79 | * Add bump_commit commands for patch, minor, major and full 80 | * Allow setting credentials via env vars 81 | * Add bump:full rake task 82 | 83 | ## 3.3.1 84 | 85 | * `bump:*` runs twice if task is defined in `Rakefile` 86 | 87 | ## 3.3.0 88 | 89 | * Allow releasing without building nor pushing to the forge, just git tagging [:clean, :tag, :bump_commit], using `build = false` 90 | 91 | ## 3.2.0 92 | 93 | * Enable bumping semantic version elements with `module:bump:` plus `patch`, `minor`, `major` 94 | 95 | ## 3.1.0 96 | 97 | * Added feature to bump dependencies `module:dependency[modulename, version]` 98 | * Add support for custom tag patterns 99 | 100 | ## 3.0.3 101 | 102 | * [Issue #11](https://github.com/maestrodev/puppet-blacksmith/issues/11) Require json and yaml in forge.rb 103 | * [Issue #10](https://github.com/maestrodev/puppet-blacksmith/issues/10) Added support to git commit the version 104 | 105 | ## 3.0.2 106 | 107 | * [Issue #9](https://github.com/maestrodev/puppet-blacksmith/issues/9) Better error messages when pushing to the Forge fails 108 | 109 | ## 3.0.1 110 | 111 | * Fix uninitialized constant Blacksmith::VERSION 112 | 113 | ## 3.0.0 114 | 115 | * Use API to upload to the Forge instead of parsing the webpage 116 | * Use a custom Blacksmith `User-Agent` 117 | 118 | ## 2.3.1 119 | 120 | * [Issue #7](https://github.com/maestrodev/puppet-blacksmith/issues/7) `module:bump_commit` fails if `Modulefile` does not exist 121 | 122 | ## 2.2.0 123 | 124 | * [Issue #6](https://github.com/maestrodev/puppet-blacksmith/issues/6) Add support for `metadata.json` 125 | 126 | ## 2.1.0 127 | 128 | * Support Puppet 3.5+ 129 | 130 | ## 2.0.0 131 | 132 | * Reworked to be a library plus rake tasks so it an be reused 133 | * `.puppetforge.yml` `forge` entry is deprecated, use `url` 134 | -------------------------------------------------------------------------------- /lib/puppet_blacksmith/version_helper.rb: -------------------------------------------------------------------------------- 1 | # Need to vendor the 'semantic' gem because Puppet hasn't appropriately 2 | # encapsulated their vendored implementation. 3 | # 4 | # For the original implementation see https://github.com/jlindsey/semantic 5 | # 6 | module Blacksmith 7 | module VersionHelper 8 | # See: http://semver.org 9 | class Version 10 | SemVerRegexp = /\A(\d+\.\d+\.\d+)(-([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?(\+([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?\Z/ 11 | attr_accessor :major, :minor, :patch, :pre, :build 12 | 13 | def initialize(version_str) 14 | raise ArgumentError, "#{version_str} is not a valid SemVer Version (http://semver.org)" unless SemVerRegexp.match?(version_str) 15 | 16 | version, parts = version_str.split '-' 17 | if !parts.nil? and parts.include? '+' 18 | @pre, @build = parts.split '+' 19 | elsif version.include? '+' 20 | version, @build = version.split '+' 21 | else 22 | @pre = parts 23 | end 24 | 25 | @major, @minor, @patch = version.split('.').map(&:to_i) 26 | end 27 | 28 | def to_a 29 | [@major, @minor, @patch, @pre, @build] 30 | end 31 | 32 | def to_s 33 | str = [@major, @minor, @patch].join '.' 34 | str << '-' << @pre unless @pre.nil? 35 | str << '+' << @build unless @build.nil? 36 | 37 | str 38 | end 39 | 40 | def to_h 41 | keys = %i[major minor patch pre build] 42 | keys.zip(to_a).to_h 43 | end 44 | 45 | alias to_hash to_h 46 | alias to_array to_a 47 | alias to_string to_s 48 | 49 | def <=>(other) 50 | other = Version.new(other) if other.is_a? String 51 | 52 | v1 = dup 53 | v2 = other.dup 54 | 55 | # The build must be excluded from the comparison, so that e.g. 1.2.3+foo and 1.2.3+bar are semantically equal. 56 | # "Build metadata SHOULD be ignored when determining version precedence". 57 | # (SemVer 2.0.0-rc.2, paragraph 10 - http://www.semver.org) 58 | v1.build = nil 59 | v2.build = nil 60 | 61 | compare_recursively(v1.to_a, v2.to_a) 62 | end 63 | 64 | def >(other) 65 | (self <=> other) == 1 66 | end 67 | 68 | def <(other) 69 | (self <=> other) == -1 70 | end 71 | 72 | def >=(other) 73 | (self <=> other) >= 0 74 | end 75 | 76 | def <=(other) 77 | (self <=> other) <= 0 78 | end 79 | 80 | def ==(other) 81 | (self <=> other) == 0 82 | end 83 | 84 | def satisfies(other_version) 85 | return true if other_version.strip == '*' 86 | 87 | parts = other_version.split(/(\d(.+)?)/, 2) 88 | comparator = parts[0].strip 89 | other_version_string = parts[1].strip 90 | 91 | begin 92 | Version.new other_version_string 93 | comparator.empty? && comparator = '==' 94 | satisfies_comparator? comparator, other_version_string 95 | rescue ArgumentError 96 | if ['<', '>', '<=', '>='].include?(comparator) 97 | satisfies_comparator? comparator, pad_version_string(other_version_string) 98 | else 99 | tilde_matches? other_version_string 100 | end 101 | end 102 | end 103 | 104 | %i[major minor patch].each do |term| 105 | define_method(:"#{term}!") { increment!(term) } 106 | end 107 | 108 | def full! 109 | env_var = 'BLACKSMITH_FULL_VERSION' 110 | begin 111 | ENV.fetch env_var 112 | rescue KeyError 113 | raise Exception, 114 | "Setting the full version requires setting the #{env_var} environment variable to the new version" 115 | end 116 | end 117 | 118 | def increment!(term) 119 | new_version = clone 120 | 121 | new_version.send(:"#{term}=", send(term) + 1) if term != :patch || @pre.nil? 122 | 123 | new_version.minor = 0 if term == :major 124 | new_version.patch = 0 if %i[major minor].include?(term) 125 | new_version.build = new_version.pre = nil 126 | 127 | new_version 128 | end 129 | 130 | private 131 | 132 | def pad_version_string(version_string) 133 | parts = version_string.split('.').reject { |x| x == '*' } 134 | parts << '0' while parts.length < 3 135 | parts.join '.' 136 | end 137 | 138 | def tilde_matches?(other_version_string) 139 | this_parts = to_a.collect(&:to_s) 140 | other_parts = other_version_string.split('.').reject { |x| x == '*' } 141 | other_parts == this_parts[0..other_parts.length - 1] 142 | end 143 | 144 | def satisfies_comparator?(comparator, other_version_string) 145 | if comparator == '~' 146 | tilde_matches? other_version_string 147 | else 148 | send comparator, other_version_string 149 | end 150 | end 151 | 152 | def compare_recursively(ary1, ary2) 153 | # Short-circuit the recursion entirely if they're just equal 154 | return 0 if ary1 == ary2 155 | 156 | a = ary1.shift 157 | b = ary2.shift 158 | 159 | # Reached the end of the arrays, equal all the way down 160 | return 0 if a.nil? and b.nil? 161 | 162 | # Mismatched types (ie. one has a pre and the other doesn't) 163 | if a.nil? and !b.nil? 164 | return 1 165 | elsif !a.nil? and b.nil? 166 | return -1 167 | end 168 | 169 | if a < b 170 | return -1 171 | elsif a > b 172 | return 1 173 | end 174 | 175 | # Versions are equal thus far, so recurse down to the next part. 176 | compare_recursively ary1, ary2 177 | end 178 | end 179 | end 180 | end 181 | -------------------------------------------------------------------------------- /spec/puppet_blacksmith/modulefile_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'Blacksmith::Modulefile' do 4 | subject { Blacksmith::Modulefile.new(path) } 5 | 6 | let(:path) { 'spec/data/metadata.json' } 7 | 8 | shared_examples_for 'metadata' do 9 | it { expect(subject.version).to eql('1.0.0') } 10 | it { expect(subject.name).to eql('test') } 11 | end 12 | 13 | context 'using different author' do 14 | subject { Blacksmith::Modulefile.new(path) } 15 | 16 | let(:path) { 'spec/data/metadata-different-author.json' } 17 | 18 | it_behaves_like 'metadata' 19 | 20 | describe 'author and namespace' do 21 | it { expect(subject.author).to eql('MaestroDev') } 22 | it { expect(subject.namespace).to eql('maestrodev') } 23 | end 24 | end 25 | 26 | context 'using no author' do 27 | subject { Blacksmith::Modulefile.new(path) } 28 | 29 | let(:path) { 'spec/data/metadata-no-author.json' } 30 | 31 | it_behaves_like 'metadata' 32 | 33 | describe 'author and namespace' do 34 | it { expect(subject.author).to eql('maestrodev') } 35 | it { expect(subject.namespace).to eql('maestrodev') } 36 | end 37 | end 38 | 39 | context 'using metadata.json' do 40 | it_behaves_like 'metadata' 41 | 42 | describe 'replace_version' do 43 | it 'replaces the version in metadata' do 44 | expected = <<-EOS 45 | { 46 | "operatingsystem_support": [ 47 | { 48 | "operatingsystem": "CentOS", 49 | "operatingsystemrelease": [ 50 | "4", 51 | "5", 52 | "6" 53 | ] 54 | } 55 | ], 56 | "requirements": [ 57 | { 58 | "name": "pe", 59 | "version_requirement": "3.2.x" 60 | }, 61 | { 62 | "name": "puppet", 63 | "version_requirement": ">=2.7.20 <7.0.0" 64 | } 65 | ], 66 | "name": "maestrodev-test", 67 | "version": "1.0.1", 68 | "source": "git://github.com/puppetlabs/puppetlabs-stdlib", 69 | "author": "maestrodev", 70 | "license": "Apache 2.0", 71 | "summary": "Puppet Module Standard Library", 72 | "description": "Standard Library for Puppet Modules", 73 | "project_page": "https://github.com/puppetlabs/puppetlabs-stdlib", 74 | "dependencies": [ 75 | { 76 | "name": "puppetlabs-stdlib", 77 | "version_requirement": ">= 3.0.0" 78 | } 79 | ] 80 | } 81 | EOS 82 | 83 | expect(JSON.parse(subject.replace_version(File.read(path), '1.0.1'))).to eql(JSON.parse(expected)) 84 | end 85 | end 86 | 87 | describe 'replace_dependency_version' do 88 | it 'replaces the version in metadata' do 89 | expected = <<-EOS 90 | { 91 | "operatingsystem_support": [ 92 | { 93 | "operatingsystem": "CentOS", 94 | "operatingsystemrelease": [ 95 | "4", 96 | "5", 97 | "6" 98 | ] 99 | } 100 | ], 101 | "requirements": [ 102 | { 103 | "name": "pe", 104 | "version_requirement": "3.2.x" 105 | }, 106 | { 107 | "name": "puppet", 108 | "version_requirement": ">=2.7.20 <7.0.0" 109 | } 110 | ], 111 | "name": "maestrodev-test", 112 | "version": "1.0.0", 113 | "source": "git://github.com/puppetlabs/puppetlabs-stdlib", 114 | "author": "maestrodev", 115 | "license": "Apache 2.0", 116 | "summary": "Puppet Module Standard Library", 117 | "description": "Standard Library for Puppet Modules", 118 | "project_page": "https://github.com/puppetlabs/puppetlabs-stdlib", 119 | "dependencies": [ 120 | { 121 | "name": "puppetlabs-stdlib", 122 | "version_requirement": ">= 4.0.0" 123 | } 124 | ] 125 | } 126 | EOS 127 | 128 | expect(JSON.parse(subject.replace_dependency_version(File.read(path), 'puppetlabs-stdlib', 129 | '>= 4.0.0'))).to eql(JSON.parse(expected)) 130 | end 131 | end 132 | end 133 | 134 | describe 'bump_to_version' do 135 | it { expect(subject.bump_to_version!('1.0.0')).to eql('1.0.0') } 136 | end 137 | 138 | describe 'increase_version' do 139 | it { expect(subject.increase_version('1.0.0')).to eql('1.0.1') } 140 | it { expect(subject.increase_version('1.0.1')).to eql('1.0.2') } 141 | it { expect { subject.increase_version('1.0') }.to raise_error(ArgumentError) } 142 | it { expect { subject.increase_version('1.0.12qwe') }.to raise_error(ArgumentError) } 143 | end 144 | 145 | describe 'bump patch version' do 146 | it { expect(subject.increase_version('1.0.0', :patch)).to eql('1.0.1') } 147 | it { expect(subject.increase_version('1.1.0', :patch)).to eql('1.1.1') } 148 | it { expect(subject.increase_version('1.1.1', :patch)).to eql('1.1.2') } 149 | it { expect(subject.increase_version('1.1.2-rc0', :patch)).to eql('1.1.2') } 150 | end 151 | 152 | describe 'bump minor version' do 153 | it { expect(subject.increase_version('1.0.0', :minor)).to eql('1.1.0') } 154 | it { expect(subject.increase_version('1.1.0', :minor)).to eql('1.2.0') } 155 | it { expect(subject.increase_version('1.1.1', :minor)).to eql('1.2.0') } 156 | it { expect(subject.increase_version('1.1.1-rc0', :minor)).to eql('1.2.0') } 157 | end 158 | 159 | describe 'bump major version' do 160 | it { expect(subject.increase_version('1.0.0', :major)).to eql('2.0.0') } 161 | it { expect(subject.increase_version('1.1.0', :major)).to eql('2.0.0') } 162 | it { expect(subject.increase_version('1.1.1', :major)).to eql('2.0.0') } 163 | it { expect(subject.increase_version('1.1.1-rc0', :major)).to eql('2.0.0') } 164 | end 165 | end 166 | -------------------------------------------------------------------------------- /lib/puppet_blacksmith/rake_tasks.rb: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'rake/tasklib' 3 | require 'puppet_blacksmith' 4 | 5 | module Blacksmith 6 | class RakeTask < ::Rake::TaskLib 7 | attr_accessor :tag_pattern, :tag_message_pattern, :tag_sign, :commit_message_pattern, :build 8 | 9 | def initialize(*args, &) 10 | @build = true 11 | @task_name = args.shift || 'blacksmith' 12 | @desc = args.shift || 'Puppet Forge utilities' 13 | define(args, &) 14 | end 15 | 16 | def git 17 | git = Blacksmith::Git.new 18 | git.tag_pattern = @tag_pattern 19 | git.tag_message_pattern = @tag_message_pattern 20 | git.commit_message_pattern = @commit_message_pattern 21 | git.tag_sign = @tag_sign 22 | 23 | git 24 | end 25 | 26 | def define(args, &task_block) 27 | yield(*[self, args].slice(0, task_block.arity)) if task_block 28 | 29 | # clear any (auto-)pre-existing task 30 | [ 31 | :build, 32 | :bump, 33 | 'bump:major', 34 | 'bump:minor', 35 | 'bump:patch', 36 | 'bump:full', 37 | :tag, 38 | :version, 39 | 'version:next', 40 | 'version:next:major', 41 | 'version:next:minor', 42 | 'version:next:patch', 43 | :bump_commit, 44 | 'bump_commit:major', 45 | 'bump_commit:minor', 46 | 'bump_commit:patch', 47 | 'bump_commit:full', 48 | :push, 49 | :clean, 50 | :release, 51 | :dependency, 52 | ].each do |t| 53 | Rake::Task.task_defined?("module:#{t}") && Rake::Task["module:#{t}"].clear 54 | end 55 | 56 | namespace :module do 57 | desc 'Build the module using puppet-modulebuilder' 58 | task :build do 59 | require 'puppet/modulebuilder' 60 | builder = Puppet::Modulebuilder::Builder.new(Dir.pwd) 61 | package_file = builder.build 62 | puts "Built #{package_file}" 63 | end 64 | 65 | namespace :bump do 66 | %i[major minor patch full].each do |level| 67 | desc "Bump module version to the next #{level.upcase} version" 68 | task level do 69 | m = Blacksmith::Modulefile.new 70 | v = m.bump!(level) 71 | puts "Bumping version from #{m.version} to #{v}" 72 | end 73 | end 74 | end 75 | 76 | desc 'Bump module to specific version number' 77 | task :bump_to_version, [:new_version] do |_t, targs| 78 | m = Blacksmith::Modulefile.new 79 | m.bump_to_version!(targs[:new_version]) 80 | puts "Bumping version to #{targs[:new_version]}" 81 | end 82 | 83 | desc 'Bump module version to the next patch' 84 | task :bump do 85 | m = Blacksmith::Modulefile.new 86 | v = m.bump_patch! 87 | puts "Bumping version from #{m.version} to #{v}" 88 | end 89 | 90 | desc 'Git tag with the current module version' 91 | task :tag do 92 | m = Blacksmith::Modulefile.new 93 | git.tag!(m.version) 94 | end 95 | 96 | namespace :version do 97 | desc 'Get next module version' 98 | task :next do 99 | m = Blacksmith::Modulefile.new 100 | puts m.increase_version(m.version, 'patch') 101 | end 102 | 103 | %i[major minor patch].each do |level| 104 | desc "Get the next #{level.upcase} version" 105 | task :"next:#{level}" do 106 | m = Blacksmith::Modulefile.new 107 | puts m.increase_version(m.version, level) 108 | end 109 | end 110 | end 111 | 112 | desc 'Get current module version' 113 | task :version do 114 | m = Blacksmith::Modulefile.new 115 | puts m.version 116 | end 117 | 118 | namespace :bump_commit do 119 | %i[major minor patch full].each do |level| 120 | desc "Bump module version to the next #{level.upcase} version and git commit" 121 | task level => :"bump:#{level}" do 122 | m = Blacksmith::Modulefile.new 123 | git.commit_modulefile!(m.version) 124 | end 125 | end 126 | end 127 | 128 | desc 'Bump version and git commit' 129 | task bump_commit: :bump do 130 | m = Blacksmith::Modulefile.new 131 | git.commit_modulefile!(m.version) 132 | end 133 | 134 | desc 'Push module to the Puppet Forge' 135 | task push: :'module:build' do 136 | m = Blacksmith::Modulefile.new 137 | forge = Blacksmith::Forge.new 138 | puts "Uploading to Puppet Forge #{m.namespace}/#{m.name}" 139 | forge.push!(m.name, nil, m.namespace, m.version) 140 | end 141 | 142 | desc 'Runs clean again' 143 | task :clean do 144 | puts 'Cleaning for module build' 145 | if Rake::Task.task_defined?(:clean) 146 | Rake::Task['clean'].execute 147 | else 148 | # identical to the clean task in puppetlabs_spec_helper on 2021-07-30 149 | # https://github.com/puppetlabs/puppetlabs_spec_helper/blob/24d7b21280a26cc682146839f41dbf1c0793e494/lib/puppetlabs_spec_helper/rake_tasks.rb#L165-L168 150 | require 'fileutils' 151 | FileUtils.rm_rf('pkg/') 152 | end 153 | end 154 | 155 | desc 'Release the Puppet module, doing a clean, build, bump_commit, tag, push and git push.' 156 | release_dependencies = if @build 157 | %i[clean module:build bump_commit tag 158 | push] 159 | else 160 | %i[clean bump_commit tag] 161 | end 162 | task release: release_dependencies do 163 | puts 'Pushing to remote git repo' 164 | git.push! 165 | end 166 | 167 | desc 'Set specific module dependency version' 168 | task :dependency, [:module_name, :version] do |_t, targs| 169 | mn = targs[:module_name] 170 | mv = targs[:version] 171 | m = Blacksmith::Modulefile.new 172 | m.bump_dep! mn, mv 173 | puts "Updated module dependency #{mn} to #{mv}" 174 | end 175 | end 176 | end 177 | end 178 | end 179 | 180 | Blacksmith::RakeTask.new 181 | -------------------------------------------------------------------------------- /lib/puppet_blacksmith/forge.rb: -------------------------------------------------------------------------------- 1 | require 'rest-client' 2 | require 'json' 3 | require 'yaml' 4 | require 'base64' 5 | 6 | module Blacksmith 7 | class Forge 8 | PUPPETLABS_FORGE = 'https://forgeapi.puppetlabs.com' 9 | CREDENTIALS_FILE_HOME = '~/.puppetforge.yml' 10 | CREDENTIALS_FILE_PROJECT = '.puppetforge.yml' 11 | FORGE_TYPE_PUPPET = 'puppet' 12 | FORGE_TYPE_ARTIFACTORY = 'artifactory' 13 | SUPPORTED_FORGE_TYPES = [FORGE_TYPE_PUPPET, FORGE_TYPE_ARTIFACTORY] 14 | DEFAULT_CREDENTIALS = { 'url' => PUPPETLABS_FORGE, 'forge_type' => FORGE_TYPE_PUPPET } 15 | HEADERS = { 'User-Agent' => "Blacksmith/#{Blacksmith::VERSION} Ruby/#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE}; #{RUBY_PLATFORM})" } 16 | 17 | attr_accessor :username, :password, :url, :forge_type, :token, :api_key 18 | 19 | def initialize(username = nil, password = nil, url = nil, forge_type = nil, token = nil, api_key = nil) 20 | self.username = username 21 | self.password = password 22 | self.token = token 23 | self.api_key = api_key 24 | RestClient.proxy = ENV.fetch('http_proxy', nil) 25 | load_credentials 26 | self.url = url unless url.nil? 27 | if %r{http(s)?://forge.puppetlabs.com}.match?(self.url) 28 | puts "Ignoring url entry in .puppetforge.yml: must point to the api server at #{PUPPETLABS_FORGE}, not the Forge webpage" 29 | self.url = PUPPETLABS_FORGE 30 | end 31 | self.forge_type = forge_type unless forge_type.nil? 32 | raise Blacksmith::Error, "Unsupported forge type: #{self.forge_type}" unless SUPPORTED_FORGE_TYPES.include?(self.forge_type) 33 | end 34 | 35 | def push!(name, package = nil, author = nil, version = nil) 36 | user = author || username 37 | unless package 38 | v = version ? Regexp.escape(version) : '.*' 39 | regex = /^#{user}-#{name}-#{v}\.tar\.gz$/ 40 | pkg = File.expand_path('pkg') 41 | f = Dir.new(pkg).select { |fn| fn.match(regex) }.last 42 | raise Errno::ENOENT, "File not found in #{pkg} with regex #{regex}" if f.nil? 43 | 44 | package = File.join(pkg, f) 45 | end 46 | raise Errno::ENOENT, "File does not exist: #{package}" unless File.exist?(package) 47 | 48 | upload(user, name, package) 49 | end 50 | 51 | private 52 | 53 | def upload(author, name, file) 54 | url = http_url(author, name, file) 55 | case forge_type 56 | when FORGE_TYPE_ARTIFACTORY 57 | RestClient::Request.execute(method: :put, url: url, payload: File.new(file, 'rb'), headers: http_headers) 58 | else 59 | RestClient::Request.execute(method: :post, url: url, payload: { file: File.new(file, 'rb') }, headers: http_headers) 60 | end 61 | rescue RestClient::Exception => e 62 | raise Blacksmith::Error, "Error uploading #{name} to the forge #{url} [#{e.message}]: #{e.response}" 63 | end 64 | 65 | def http_url(author, name, file) 66 | case forge_type 67 | when FORGE_TYPE_ARTIFACTORY 68 | "#{url}/#{author}/#{name}/#{File.basename(file)}" 69 | else 70 | "#{url}/v3/releases" 71 | end 72 | end 73 | 74 | def http_headers 75 | case forge_type 76 | when FORGE_TYPE_ARTIFACTORY 77 | if api_key 78 | HEADERS.merge({ 'X-JFrog-Art-Api' => api_key }) 79 | elsif token 80 | HEADERS.merge({ 'Authorization' => "Bearer #{token}" }) 81 | else 82 | HEADERS.merge({ 'Authorization' => 'Basic ' + Base64.strict_encode64("#{username}:#{password}") }) 83 | end 84 | else 85 | HEADERS.merge({ 'Authorization' => "Bearer #{api_key || token}" }) 86 | end 87 | end 88 | 89 | def load_credentials 90 | file_credentials = load_credentials_from_file 91 | env_credentials = load_credentials_from_env 92 | 93 | credentials = DEFAULT_CREDENTIALS.merge file_credentials 94 | credentials = credentials.merge env_credentials 95 | 96 | self.username = credentials['username'] if credentials['username'] 97 | self.password = credentials['password'] if credentials['password'] 98 | self.token = credentials['token'] if credentials['token'] 99 | self.api_key = credentials['api_key'] if credentials['api_key'] 100 | if credentials['forge'] 101 | # deprecated 102 | puts "'forge' entry is deprecated in .puppetforge.yml, use 'url'" 103 | self.url = credentials['forge'] 104 | end 105 | self.url = credentials['url'] if credentials['url'] 106 | self.forge_type = credentials['forge_type'] if credentials['forge_type'] 107 | 108 | unless (username && password) || token || api_key 109 | raise Blacksmith::Error, <<~EOS 110 | Could not find Puppet Forge credentials! 111 | 112 | Please set the environment variables 113 | BLACKSMITH_FORGE_URL 114 | BLACKSMITH_FORGE_TYPE 115 | BLACKSMITH_FORGE_USERNAME 116 | BLACKSMITH_FORGE_PASSWORD 117 | BLACKSMITH_FORGE_TOKEN 118 | BLACKSMITH_FORGE_API_KEY 119 | 120 | or create the file '#{CREDENTIALS_FILE_PROJECT}' or '#{CREDENTIALS_FILE_HOME}' 121 | with content similiar to: 122 | 123 | --- 124 | url: https://forgeapi.puppetlabs.com 125 | username: myuser 126 | password: mypassword 127 | 128 | EOS 129 | end 130 | end 131 | 132 | def load_credentials_from_file 133 | credentials_file = [File.join(Dir.pwd, CREDENTIALS_FILE_PROJECT), File.expand_path(CREDENTIALS_FILE_HOME)].select { |file| File.exist?(file) }.first 134 | 135 | if credentials_file 136 | YAML.load_file(credentials_file) 137 | else 138 | {} 139 | end 140 | end 141 | 142 | def load_credentials_from_env 143 | credentials = {} 144 | 145 | credentials['username'] = ENV['BLACKSMITH_FORGE_USERNAME'] if ENV['BLACKSMITH_FORGE_USERNAME'] 146 | 147 | credentials['password'] = ENV['BLACKSMITH_FORGE_PASSWORD'] if ENV['BLACKSMITH_FORGE_PASSWORD'] 148 | 149 | credentials['url'] = ENV['BLACKSMITH_FORGE_URL'] if ENV['BLACKSMITH_FORGE_URL'] 150 | 151 | credentials['forge_type'] = ENV['BLACKSMITH_FORGE_TYPE'] if ENV['BLACKSMITH_FORGE_TYPE'] 152 | 153 | credentials['token'] = ENV['BLACKSMITH_FORGE_TOKEN'] if ENV['BLACKSMITH_FORGE_TOKEN'] 154 | 155 | credentials['api_key'] = ENV['BLACKSMITH_FORGE_API_KEY'] if ENV['BLACKSMITH_FORGE_API_KEY'] 156 | 157 | credentials 158 | end 159 | end 160 | end 161 | -------------------------------------------------------------------------------- /.rubocop_todo.yml: -------------------------------------------------------------------------------- 1 | # This configuration was generated by 2 | # `rubocop --auto-gen-config --no-auto-gen-timestamp` 3 | # using RuboCop version 1.81.7. 4 | # The point is for the user to remove these configuration records 5 | # one by one as the offenses are removed from the code base. 6 | # Note that changes in the inspected code, or installation of new 7 | # versions of RuboCop, may require this file to be generated again. 8 | 9 | # Offense count: 1 10 | # This cop supports unsafe autocorrection (--autocorrect-all). 11 | # Configuration parameters: RequireParenthesesForMethodChains. 12 | Lint/AmbiguousRange: 13 | Exclude: 14 | - 'lib/puppet_blacksmith/version_helper.rb' 15 | 16 | # Offense count: 3 17 | # Configuration parameters: AllowComments, AllowEmptyLambdas. 18 | Lint/EmptyBlock: 19 | Exclude: 20 | - 'Rakefile' 21 | - 'spec/puppet_blacksmith/git_spec.rb' 22 | 23 | # Offense count: 1 24 | # Configuration parameters: AllowedParentClasses. 25 | Lint/MissingSuper: 26 | Exclude: 27 | - 'lib/puppet_blacksmith/rake_tasks.rb' 28 | 29 | # Offense count: 1 30 | # This cop supports unsafe autocorrection (--autocorrect-all). 31 | # Configuration parameters: AllowedImplicitNamespaces. 32 | # AllowedImplicitNamespaces: Gem 33 | Lint/RaiseException: 34 | Exclude: 35 | - 'lib/puppet_blacksmith/version_helper.rb' 36 | 37 | # Offense count: 1 38 | Naming/ConstantName: 39 | Exclude: 40 | - 'lib/puppet_blacksmith/version_helper.rb' 41 | 42 | # Offense count: 4 43 | # Configuration parameters: ForbiddenDelimiters. 44 | # ForbiddenDelimiters: (?i-mx:(^|\s)(EO[A-Z]{1}|END)(\s|$)) 45 | Naming/HeredocDelimiterNaming: 46 | Exclude: 47 | - 'Rakefile' 48 | - 'lib/puppet_blacksmith/forge.rb' 49 | - 'spec/puppet_blacksmith/modulefile_spec.rb' 50 | 51 | # Offense count: 2 52 | # Configuration parameters: NamePrefix, ForbiddenPrefixes, AllowedMethods, MethodDefinitionMacros, UseSorbetSigs. 53 | # NamePrefix: is_, has_, have_, does_ 54 | # ForbiddenPrefixes: is_, has_, have_, does_ 55 | # AllowedMethods: is_a? 56 | # MethodDefinitionMacros: define_method, define_singleton_method 57 | Naming/PredicatePrefix: 58 | Exclude: 59 | - 'spec/**/*' 60 | - 'lib/puppet_blacksmith/git.rb' 61 | 62 | # Offense count: 2 63 | # This cop supports unsafe autocorrection (--autocorrect-all). 64 | Performance/Detect: 65 | Exclude: 66 | - 'lib/puppet_blacksmith/forge.rb' 67 | 68 | # Offense count: 8 69 | # Configuration parameters: Prefixes, AllowedPatterns. 70 | # Prefixes: when, with, without 71 | RSpec/ContextWording: 72 | Exclude: 73 | - 'spec/puppet_blacksmith/forge_shared.rb' 74 | - 'spec/puppet_blacksmith/git_spec.rb' 75 | - 'spec/puppet_blacksmith/modulefile_spec.rb' 76 | 77 | # Offense count: 3 78 | # Configuration parameters: CountAsOne. 79 | RSpec/ExampleLength: 80 | Max: 40 81 | 82 | # Offense count: 1 83 | RSpec/ExpectInHook: 84 | Exclude: 85 | - 'spec/puppet_blacksmith/git_spec.rb' 86 | 87 | # Offense count: 8 88 | # Configuration parameters: AllowSubject. 89 | RSpec/MultipleMemoizedHelpers: 90 | Max: 11 91 | 92 | # Offense count: 26 93 | # Configuration parameters: EnforcedStyle, IgnoreSharedExamples. 94 | # SupportedStyles: always, named_only 95 | RSpec/NamedSubject: 96 | Exclude: 97 | - 'spec/puppet_blacksmith/forge_spec.rb' 98 | - 'spec/puppet_blacksmith/modulefile_spec.rb' 99 | 100 | # Offense count: 4 101 | # Configuration parameters: AllowedGroups. 102 | RSpec/NestedGroups: 103 | Max: 4 104 | 105 | # Offense count: 3 106 | # Configuration parameters: AllowedPatterns. 107 | # AllowedPatterns: ^expect_, ^assert_ 108 | RSpec/NoExpectationExample: 109 | Exclude: 110 | - 'spec/puppet_blacksmith/forge_shared.rb' 111 | - 'spec/puppet_blacksmith/git_spec.rb' 112 | 113 | # Offense count: 2 114 | RSpec/RepeatedExampleGroupDescription: 115 | Exclude: 116 | - 'spec/puppet_blacksmith/git_spec.rb' 117 | 118 | # Offense count: 1 119 | RSpec/SubjectStub: 120 | Exclude: 121 | - 'spec/puppet_blacksmith/git_spec.rb' 122 | 123 | # Offense count: 4 124 | # Configuration parameters: IgnoreNameless, IgnoreSymbolicNames. 125 | RSpec/VerifiedDoubles: 126 | Exclude: 127 | - 'spec/puppet_blacksmith/git_spec.rb' 128 | 129 | # Offense count: 1 130 | Security/Open: 131 | Exclude: 132 | - 'spec/puppet_blacksmith/git_spec.rb' 133 | 134 | # Offense count: 4 135 | # This cop supports unsafe autocorrection (--autocorrect-all). 136 | # Configuration parameters: EnforcedStyle. 137 | # SupportedStyles: always, conditionals 138 | Style/AndOr: 139 | Exclude: 140 | - 'lib/puppet_blacksmith/version_helper.rb' 141 | 142 | # Offense count: 4 143 | # Configuration parameters: AllowedConstants. 144 | Style/Documentation: 145 | Exclude: 146 | - 'spec/**/*' 147 | - 'test/**/*' 148 | - 'lib/puppet_blacksmith/forge.rb' 149 | - 'lib/puppet_blacksmith/git.rb' 150 | - 'lib/puppet_blacksmith/modulefile.rb' 151 | - 'lib/puppet_blacksmith/rake_tasks.rb' 152 | 153 | # Offense count: 18 154 | # This cop supports unsafe autocorrection (--autocorrect-all). 155 | # Configuration parameters: EnforcedStyle. 156 | # SupportedStyles: always, always_true, never 157 | Style/FrozenStringLiteralComment: 158 | Enabled: false 159 | 160 | # Offense count: 1 161 | # This cop supports safe autocorrection (--autocorrect). 162 | # Configuration parameters: MinBodyLength, AllowConsecutiveConditionals. 163 | Style/GuardClause: 164 | Exclude: 165 | - 'lib/puppet_blacksmith/forge.rb' 166 | 167 | # Offense count: 10 168 | # This cop supports unsafe autocorrection (--autocorrect-all). 169 | # Configuration parameters: EnforcedStyle. 170 | # SupportedStyles: literals, strict 171 | Style/MutableConstant: 172 | Exclude: 173 | - 'lib/puppet_blacksmith/forge.rb' 174 | - 'lib/puppet_blacksmith/modulefile.rb' 175 | - 'lib/puppet_blacksmith/version.rb' 176 | 177 | # Offense count: 1 178 | # This cop supports unsafe autocorrection (--autocorrect-all). 179 | # Configuration parameters: EnforcedStyle, AllowedMethods, AllowedPatterns. 180 | # SupportedStyles: predicate, comparison 181 | Style/NumericPredicate: 182 | Exclude: 183 | - 'spec/**/*' 184 | - 'lib/puppet_blacksmith/version_helper.rb' 185 | 186 | # Offense count: 1 187 | # This cop supports unsafe autocorrection (--autocorrect-all). 188 | # Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength. 189 | # AllowedMethods: present?, blank?, presence, try, try! 190 | Style/SafeNavigation: 191 | Exclude: 192 | - 'lib/puppet_blacksmith/git.rb' 193 | 194 | # Offense count: 2 195 | # This cop supports unsafe autocorrection (--autocorrect-all). 196 | # Configuration parameters: Mode. 197 | Style/StringConcatenation: 198 | Exclude: 199 | - 'lib/puppet_blacksmith/forge.rb' 200 | - 'lib/puppet_blacksmith/git.rb' 201 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | puppet-blacksmith 2 | ================= 3 | 4 | [![License](https://img.shields.io/github/license/voxpupuli/puppet-blacksmith.svg)](https://github.com/voxpupuli/puppet-blacksmith/blob/master/LICENSE) 5 | [![Test](https://github.com/voxpupuli/puppet-blacksmith/actions/workflows/test.yml/badge.svg)](https://github.com/voxpupuli/puppet-blacksmith/actions/workflows/test.yml) 6 | [![Release](https://github.com/voxpupuli/puppet-blacksmith/actions/workflows/release.yml/badge.svg)](https://github.com/voxpupuli/puppet-blacksmith/actions/workflows/release.yml) 7 | [![RubyGem Version](https://img.shields.io/gem/v/puppet-blacksmith.svg)](https://rubygems.org/gems/puppet-blacksmith) 8 | [![RubyGem Downloads](https://img.shields.io/gem/dt/puppet-blacksmith.svg)](https://rubygems.org/gems/puppet-blacksmith) 9 | [![Donated by Carlos Sanchez](https://img.shields.io/badge/donated%20by-Carlos%20Sanchez-fb7047.svg)](#transfer-notice) 10 | 11 | Ruby Gem with several Puppet Module utilities 12 | 13 | ![I don't always release my Puppet modules, but when I do I push them directly to the Forge](https://raw.github.com/voxpupuli/puppet-blacksmith/gh-pages/dos-equis.jpg) 14 | 15 | # Rake tasks 16 | 17 | ## Installation 18 | 19 | Install the gem 20 | 21 | ```console 22 | $ gem install puppet-blacksmith 23 | ``` 24 | 25 | Add to your Rakefile 26 | 27 | ```ruby 28 | require 'puppet_blacksmith/rake_tasks' 29 | ``` 30 | 31 | And you can start using the Rake tasks. Note that you might have to delete `.rake_t_cache` 32 | before the tasks appear in the output of `rake -T`. 33 | 34 | ## Tasks 35 | 36 | Rake tasks included: 37 | 38 | | task | description | 39 | | ------------------ | ----------- | 40 | | module:build | Build the module using puppet-modulebuilder | 41 | | module:bump | Bump module version to the next patch | 42 | | module:bump:patch | Bump module version to the next patch | 43 | | module:bump:minor | Bump module version to the next minor version | 44 | | module:bump:major | Bump module version to the next major version | 45 | | module:bump:full | Bump module version to the version set in the BLACKSMITH_FULL_VERSION env variable | 46 | | module:bump_commit | Bump version and git commit | 47 | | module:bump_commit:patch | Bump module version to the next patch and git commit | 48 | | module:bump_commit:minor | Bump module version to the next minor version and git commit | 49 | | module:bump_commit:major | Bump module version to the next major version and git commit | 50 | | module:bump_commit:full | Bump module version to the version set in the BLACKSMITH_FULL_VERSION env variable and git commit | 51 | | module:bump_to_version\[:new\_version\] | Bump module version to _new\_version_ | 52 | | module:clean | Runs clean again | 53 | | module:dependency[modulename, version] | Updates the module version of a specific dependency | 54 | | module:push | Push module to the Puppet Forge | 55 | | module:release | Release the Puppet module, doing a clean, build, bump_commit, tag, push and git push | 56 | | module:tag | Git tag with the current module version | 57 | | module:version | Get the current module version | 58 | | module:version:next | Get the next patch module version | 59 | | module:version:next:patch | Get the next patch module version | 60 | | module:version:next:minor | Get the next minor module version | 61 | | module:version:next:major | Get the next major module version | 62 | 63 | ### Full release 64 | 65 | Do everything needed to push to the Forge with just one command 66 | 67 | ```console 68 | $ rake module:release 69 | ``` 70 | 71 | ### Bump the version of a module 72 | 73 | Bump your `metadata.json` to the next version 74 | 75 | ```console 76 | $ rake module:bump 77 | ``` 78 | 79 | ### Push a module to a repository 80 | 81 | Run rake. Ensure you are doing it in a clean working folder or the puppet module builder will package all the unnecessary files. 82 | 83 | ```console 84 | $ rake module:push 85 | ``` 86 | 87 | #### Configuring to push a module to the Puppet Forge 88 | 89 | Configure your credentials in `~/.puppetforge.yml`. Forge API keys may be created and revoked from a user's 90 | profile on the [Forge website](https://forge.puppet.com/), where other profile details are managed. 91 | 92 | ```yaml 93 | --- 94 | api_key: myAPIkey 95 | ``` 96 | 97 | Or set the equivalent environment variable in your shell 98 | 99 | ```bash 100 | export BLACKSMITH_FORGE_API_KEY=myAPIkey 101 | ``` 102 | 103 | 104 | #### Configuring to push a module to a JFrog Artifactory 105 | 106 | Configure your credentials in `~/.puppetforge.yml` or within the project's root directory in `./puppetforge.yml` 107 | 108 | ```yaml 109 | --- 110 | url: https://artifactory.example.com 111 | forge_type: artifactory 112 | username: myuser 113 | password: mypassword 114 | ``` 115 | 116 | Or set the equivalent environment variables in your shell 117 | 118 | ```bash 119 | export BLACKSMITH_FORGE_URL=https://artifactory.example.com 120 | export BLACKSMITH_FORGE_TYPE=artifactory 121 | export BLACKSMITH_FORGE_USERNAME=myuser 122 | export BLACKSMITH_FORGE_PASSWORD=mypassword 123 | ``` 124 | 125 | Alternatively, you can generate an API Key on the Artifactory profile page and us that in `puppetforge.yml`. 126 | 127 | ```yaml 128 | --- 129 | url: https://artifactory.example.com 130 | forge_type: artifactory 131 | api_key: myAPIkey 132 | ```` 133 | 134 | Or via an environment variable: 135 | 136 | ```bash 137 | export BLACKSMITH_FORGE_API_KEY=myAPIkey 138 | ``` 139 | 140 | # Customizing tasks 141 | 142 | In your Rakefile: 143 | 144 | ```ruby 145 | require 'puppet_blacksmith/rake_tasks' 146 | Blacksmith::RakeTask.new do |t| 147 | t.tag_pattern = "v%s" # Use a custom pattern with git tag. %s is replaced with the version number. 148 | t.build = false # do not build the module nor push it to the Forge, just do the tagging [:clean, :tag, :bump_commit] 149 | end 150 | ```` 151 | 152 | # GPG signed tags 153 | 154 | In your Rakefile: 155 | 156 | ```ruby 157 | require 'puppet_blacksmith/rake_tasks' 158 | Blacksmith::RakeTask.new do |t| 159 | t.tag_message_pattern = "Version %s" # Signed tags must have a message 160 | t.tag_sign = true # enable GPG signing 161 | end 162 | ``` 163 | 164 | # Testing blacksmith 165 | 166 | ```bash 167 | bundle install 168 | bundle exec rake 169 | ``` 170 | 171 | ## Transfer Notice 172 | 173 | This plugin was originally authored by [Carlos Sanchez](https://blog.csanchez.org/). 174 | The maintainer preferred that Vox Pupuli take ownership of the module for future improvement and maintenance. 175 | Existing pull requests and issues were transferred over, please fork and continue to contribute at https://github.com/voxpupuli/beaker-vmware 176 | 177 | Previously: https://github.com/puppetlabs/beaker-vmware 178 | 179 | ## License 180 | 181 | This gem is licensed under the Apache-2 license. 182 | 183 | ## Release information 184 | 185 | To make a new release, please do: 186 | * update the version in lib/puppet_blacksmith/version.rb 187 | * Install gems with `bundle install --with release --path .vendor` 188 | * generate the changelog with `bundle exec rake changelog` 189 | * Check if the new version matches the closed issues/PRs in the changelog 190 | * Create a PR with it 191 | * After it got merged, push a tag. GitHub actions will do the actual release to rubygems and GitHub Packages 192 | -------------------------------------------------------------------------------- /spec/data/response.json: -------------------------------------------------------------------------------- 1 | { 2 | "slug": "maestrodev/test/1.0.5165510", 3 | "_links": { 4 | "self": { 5 | "href": null 6 | } 7 | }, 8 | "id": 11099, 9 | "module": { 10 | "id": 933, 11 | "name": "test", 12 | "homepage_url": "http://github.com/maestrodev/puppet-test", 13 | "source_url": "http://github.com/maestrodev/puppetforge", 14 | "issues_url": "", 15 | "commit_feed_url": "", 16 | "owner": { 17 | "id": 1185, 18 | "username": "maestrodev", 19 | "email": "info@maestrodev.com", 20 | "display_name": "MaestroDev", 21 | "release_count": 377, 22 | "module_count": 32, 23 | "created_at": "2012-05-24T05:37:32-07:00", 24 | "updated_at": "2012-11-09T22:11:21-08:00" 25 | }, 26 | "tags": [], 27 | "downloads": 30409, 28 | "created_at": "2013-01-25T12:53:13-08:00", 29 | "updated_at": "2014-09-18T10:47:45-07:00", 30 | "current_release": { 31 | "id": 11096, 32 | "version": "1.0.8466228", 33 | "metadata": { 34 | "name": "maestrodev-test", 35 | "version": "1.0.8466228", 36 | "source": "http://github.com/maestrodev/puppet-test", 37 | "author": "maestrodev", 38 | "license": "Apache License, Version 2.0", 39 | "summary": "Testing Puppet module operations", 40 | "description": "Testing Puppet module operations", 41 | "project_page": "http://github.com/maestrodev/puppet-test", 42 | "dependencies": [], 43 | "types": [], 44 | "checksums": {} 45 | }, 46 | "downloads": 0, 47 | "file_size": 375, 48 | "file_md5": "c8e550ca457983c92296eb99180b7052", 49 | "created_at": "2014-09-18T09:48:56-07:00", 50 | "updated_at": "2014-09-18T09:48:56-07:00", 51 | "readme": null, 52 | "changelog": null, 53 | "license": null 54 | }, 55 | "releases": [ 56 | { 57 | "slug": "maestrodev-test-1.0.8466228", 58 | "key": "1.0.8466228", 59 | "_links": { 60 | "self": { 61 | "href": null 62 | } 63 | } 64 | }, 65 | { 66 | "slug": "maestrodev-test-1.0.7876831", 67 | "key": "1.0.7876831", 68 | "_links": { 69 | "self": { 70 | "href": null 71 | } 72 | } 73 | }, 74 | { 75 | "slug": "maestrodev-test-1.0.5165510", 76 | "key": "1.0.5165510", 77 | "_links": { 78 | "self": { 79 | "href": null 80 | } 81 | } 82 | }, 83 | { 84 | "slug": "maestrodev-test-1.0.3620912", 85 | "key": "1.0.3620912", 86 | "_links": { 87 | "self": { 88 | "href": null 89 | } 90 | } 91 | }, 92 | { 93 | "slug": "maestrodev-test-1.0.26", 94 | "key": "1.0.26", 95 | "_links": { 96 | "self": { 97 | "href": null 98 | } 99 | } 100 | }, 101 | { 102 | "slug": "maestrodev-test-1.0.25", 103 | "key": "1.0.25", 104 | "_links": { 105 | "self": { 106 | "href": null 107 | } 108 | } 109 | }, 110 | { 111 | "slug": "maestrodev-test-1.0.24", 112 | "key": "1.0.24", 113 | "_links": { 114 | "self": { 115 | "href": null 116 | } 117 | } 118 | }, 119 | { 120 | "slug": "maestrodev-test-1.0.23", 121 | "key": "1.0.23", 122 | "_links": { 123 | "self": { 124 | "href": null 125 | } 126 | } 127 | }, 128 | { 129 | "slug": "maestrodev-test-1.0.22", 130 | "key": "1.0.22", 131 | "_links": { 132 | "self": { 133 | "href": null 134 | } 135 | } 136 | }, 137 | { 138 | "slug": "maestrodev-test-1.0.21", 139 | "key": "1.0.21", 140 | "_links": { 141 | "self": { 142 | "href": null 143 | } 144 | } 145 | }, 146 | { 147 | "slug": "maestrodev-test-1.0.19", 148 | "key": "1.0.19", 149 | "_links": { 150 | "self": { 151 | "href": null 152 | } 153 | } 154 | }, 155 | { 156 | "slug": "maestrodev-test-1.0.18", 157 | "key": "1.0.18", 158 | "_links": { 159 | "self": { 160 | "href": null 161 | } 162 | } 163 | }, 164 | { 165 | "slug": "maestrodev-test-1.0.16", 166 | "key": "1.0.16", 167 | "_links": { 168 | "self": { 169 | "href": null 170 | } 171 | } 172 | }, 173 | { 174 | "slug": "maestrodev-test-1.0.15", 175 | "key": "1.0.15", 176 | "_links": { 177 | "self": { 178 | "href": null 179 | } 180 | } 181 | }, 182 | { 183 | "slug": "maestrodev-test-1.0.14", 184 | "key": "1.0.14", 185 | "_links": { 186 | "self": { 187 | "href": null 188 | } 189 | } 190 | }, 191 | { 192 | "slug": "maestrodev-test-1.0.13", 193 | "key": "1.0.13", 194 | "_links": { 195 | "self": { 196 | "href": null 197 | } 198 | } 199 | }, 200 | { 201 | "slug": "maestrodev-test-1.0.12", 202 | "key": "1.0.12", 203 | "_links": { 204 | "self": { 205 | "href": null 206 | } 207 | } 208 | }, 209 | { 210 | "slug": "maestrodev-test-1.0.11", 211 | "key": "1.0.11", 212 | "_links": { 213 | "self": { 214 | "href": null 215 | } 216 | } 217 | }, 218 | { 219 | "slug": "maestrodev-test-1.0.10", 220 | "key": "1.0.10", 221 | "_links": { 222 | "self": { 223 | "href": null 224 | } 225 | } 226 | }, 227 | { 228 | "slug": "maestrodev-test-1.0.9", 229 | "key": "1.0.9", 230 | "_links": { 231 | "self": { 232 | "href": null 233 | } 234 | } 235 | }, 236 | { 237 | "slug": "maestrodev-test-1.0.8", 238 | "key": "1.0.8", 239 | "_links": { 240 | "self": { 241 | "href": null 242 | } 243 | } 244 | }, 245 | { 246 | "slug": "maestrodev-test-1.0.7", 247 | "key": "1.0.7", 248 | "_links": { 249 | "self": { 250 | "href": null 251 | } 252 | } 253 | }, 254 | { 255 | "slug": "maestrodev-test-1.0.6", 256 | "key": "1.0.6", 257 | "_links": { 258 | "self": { 259 | "href": null 260 | } 261 | } 262 | }, 263 | { 264 | "slug": "maestrodev-test-1.0.5", 265 | "key": "1.0.5", 266 | "_links": { 267 | "self": { 268 | "href": null 269 | } 270 | } 271 | }, 272 | { 273 | "slug": "maestrodev-test-1.0.4", 274 | "key": "1.0.4", 275 | "_links": { 276 | "self": { 277 | "href": null 278 | } 279 | } 280 | }, 281 | { 282 | "slug": "maestrodev-test-1.0.3", 283 | "key": "1.0.3", 284 | "_links": { 285 | "self": { 286 | "href": null 287 | } 288 | } 289 | }, 290 | { 291 | "slug": "maestrodev-test-1.0.2", 292 | "key": "1.0.2", 293 | "_links": { 294 | "self": { 295 | "href": null 296 | } 297 | } 298 | }, 299 | { 300 | "slug": "maestrodev-test-1.0.1", 301 | "key": "1.0.1", 302 | "_links": { 303 | "self": { 304 | "href": null 305 | } 306 | } 307 | }, 308 | { 309 | "slug": "maestrodev-test-1.0.0", 310 | "key": "1.0.0", 311 | "_links": { 312 | "self": { 313 | "href": null 314 | } 315 | } 316 | } 317 | ] 318 | }, 319 | "version": "1.0.5165510", 320 | "metadata": { 321 | "name": "maestrodev-test", 322 | "version": "1.0.5165510", 323 | "source": "http://github.com/maestrodev/puppet-test", 324 | "author": "maestrodev", 325 | "license": "Apache License, Version 2.0", 326 | "summary": "Testing Puppet module operations", 327 | "description": "Testing Puppet module operations", 328 | "project_page": "http://github.com/maestrodev/puppet-test", 329 | "dependencies": [], 330 | "types": [], 331 | "checksums": {} 332 | }, 333 | "downloads": 0, 334 | "file_size": 374, 335 | "file_md5": "181a8a83140de2bac9475f5c0142079a", 336 | "created_at": "2014-09-18T10:47:45-07:00", 337 | "updated_at": "2014-09-18T10:47:45-07:00", 338 | "readme": null, 339 | "changelog": null, 340 | "license": null 341 | } 342 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [9.0.0](https://github.com/voxpupuli/puppet-blacksmith/tree/9.0.0) (2025-09-26) 4 | 5 | [Full Changelog](https://github.com/voxpupuli/puppet-blacksmith/compare/v8.3.0...9.0.0) 6 | 7 | **Breaking changes:** 8 | 9 | - Require Ruby 3.2 or newer [\#145](https://github.com/voxpupuli/puppet-blacksmith/pull/145) ([bastelfreak](https://github.com/bastelfreak)) 10 | 11 | ## [v8.3.0](https://github.com/voxpupuli/puppet-blacksmith/tree/v8.3.0) (2025-07-18) 12 | 13 | [Full Changelog](https://github.com/voxpupuli/puppet-blacksmith/compare/v8.2.0...v8.3.0) 14 | 15 | **Implemented enhancements:** 16 | 17 | - remove unused puppet testing dependency [\#141](https://github.com/voxpupuli/puppet-blacksmith/pull/141) ([bastelfreak](https://github.com/bastelfreak)) 18 | 19 | **Merged pull requests:** 20 | 21 | - cucumber: Allow 10.x [\#138](https://github.com/voxpupuli/puppet-blacksmith/pull/138) ([dependabot[bot]](https://github.com/apps/dependabot)) 22 | 23 | ## [v8.2.0](https://github.com/voxpupuli/puppet-blacksmith/tree/v8.2.0) (2025-06-27) 24 | 25 | [Full Changelog](https://github.com/voxpupuli/puppet-blacksmith/compare/v8.1.0...v8.2.0) 26 | 27 | **Merged pull requests:** 28 | 29 | - Update base64 requirement from ~\> 0.2.0 to \>= 0.2, \< 0.4 [\#137](https://github.com/voxpupuli/puppet-blacksmith/pull/137) ([dependabot[bot]](https://github.com/apps/dependabot)) 30 | 31 | ## [v8.1.0](https://github.com/voxpupuli/puppet-blacksmith/tree/v8.1.0) (2025-01-20) 32 | 33 | [Full Changelog](https://github.com/voxpupuli/puppet-blacksmith/compare/v8.0.0...v8.1.0) 34 | 35 | **Implemented enhancements:** 36 | 37 | - Add Ruby 3.4 support [\#132](https://github.com/voxpupuli/puppet-blacksmith/pull/132) ([bastelfreak](https://github.com/bastelfreak)) 38 | 39 | **Merged pull requests:** 40 | 41 | - Update voxpupuli-rubocop requirement from ~\> 2.8.0 to ~\> 3.0.0 [\#131](https://github.com/voxpupuli/puppet-blacksmith/pull/131) ([dependabot[bot]](https://github.com/apps/dependabot)) 42 | 43 | ## [v8.0.0](https://github.com/voxpupuli/puppet-blacksmith/tree/v8.0.0) (2024-10-21) 44 | 45 | [Full Changelog](https://github.com/voxpupuli/puppet-blacksmith/compare/v7.1.0...v8.0.0) 46 | 47 | **Breaking changes:** 48 | 49 | - puppet-modulebuilder: Switch to 2.x; use file allowlist when building modules [\#125](https://github.com/voxpupuli/puppet-blacksmith/pull/125) ([bastelfreak](https://github.com/bastelfreak)) 50 | 51 | **Implemented enhancements:** 52 | 53 | - Add Ruby 3.3 to CI [\#129](https://github.com/voxpupuli/puppet-blacksmith/pull/129) ([bastelfreak](https://github.com/bastelfreak)) 54 | 55 | ## [v7.1.0](https://github.com/voxpupuli/puppet-blacksmith/tree/v7.1.0) (2024-10-21) 56 | 57 | [Full Changelog](https://github.com/voxpupuli/puppet-blacksmith/compare/v7.0.0...v7.1.0) 58 | 59 | **Implemented enhancements:** 60 | 61 | - voxpupuli-rubocop: Switch to 2.8.0 [\#126](https://github.com/voxpupuli/puppet-blacksmith/pull/126) ([bastelfreak](https://github.com/bastelfreak)) 62 | 63 | **Merged pull requests:** 64 | 65 | - Update cucumber requirement from ~\> 8.0 to ~\> 9.0 [\#123](https://github.com/voxpupuli/puppet-blacksmith/pull/123) ([dependabot[bot]](https://github.com/apps/dependabot)) 66 | - Update voxpupuli-rubocop requirement from ~\> 1.2 to ~\> 2.0 [\#121](https://github.com/voxpupuli/puppet-blacksmith/pull/121) ([dependabot[bot]](https://github.com/apps/dependabot)) 67 | 68 | ## [v7.0.0](https://github.com/voxpupuli/puppet-blacksmith/tree/v7.0.0) (2023-05-09) 69 | 70 | [Full Changelog](https://github.com/voxpupuli/puppet-blacksmith/compare/v6.1.1...v7.0.0) 71 | 72 | **Breaking changes:** 73 | 74 | - Remove oauth username/password login [\#119](https://github.com/voxpupuli/puppet-blacksmith/pull/119) ([bastelfreak](https://github.com/bastelfreak)) 75 | - Drop Ruby 2.4/2.5/2.6 support; Implement RuboCop [\#109](https://github.com/voxpupuli/puppet-blacksmith/pull/109) ([bastelfreak](https://github.com/bastelfreak)) 76 | 77 | **Implemented enhancements:** 78 | 79 | - Enable Tests on Ruby 3.1/3.2 [\#108](https://github.com/voxpupuli/puppet-blacksmith/pull/108) ([bastelfreak](https://github.com/bastelfreak)) 80 | - Switch from {Dir,File}.exists? to exist? [\#107](https://github.com/voxpupuli/puppet-blacksmith/pull/107) ([tuxmea](https://github.com/tuxmea)) 81 | - Update to API v3 [\#105](https://github.com/voxpupuli/puppet-blacksmith/pull/105) ([binford2k](https://github.com/binford2k)) 82 | 83 | **Closed issues:** 84 | 85 | - Incompatible with ruby 3.2 \(and Puppet 8\) [\#106](https://github.com/voxpupuli/puppet-blacksmith/issues/106) 86 | - Tag creation in wrong order [\#57](https://github.com/voxpupuli/puppet-blacksmith/issues/57) 87 | 88 | **Merged pull requests:** 89 | 90 | - GCG: Add faraday-retry dep [\#117](https://github.com/voxpupuli/puppet-blacksmith/pull/117) ([bastelfreak](https://github.com/bastelfreak)) 91 | - add dummy CI job we can depend on [\#116](https://github.com/voxpupuli/puppet-blacksmith/pull/116) ([bastelfreak](https://github.com/bastelfreak)) 92 | - CI: Build gems with strictness and verbosity & gemspec: Add dependency version constraints [\#115](https://github.com/voxpupuli/puppet-blacksmith/pull/115) ([bastelfreak](https://github.com/bastelfreak)) 93 | - puppet-modulebuilder: Switch to 1.x [\#114](https://github.com/voxpupuli/puppet-blacksmith/pull/114) ([bastelfreak](https://github.com/bastelfreak)) 94 | 95 | ## [v6.1.1](https://github.com/voxpupuli/puppet-blacksmith/tree/v6.1.1) (2021-08-05) 96 | 97 | [Full Changelog](https://github.com/voxpupuli/puppet-blacksmith/compare/v6.1.0...v6.1.1) 98 | 99 | **Fixed bugs:** 100 | 101 | - GHA: Set git user/email [\#100](https://github.com/voxpupuli/puppet-blacksmith/pull/100) ([bastelfreak](https://github.com/bastelfreak)) 102 | - Implement `clean` rake task [\#97](https://github.com/voxpupuli/puppet-blacksmith/pull/97) ([bastelfreak](https://github.com/bastelfreak)) 103 | 104 | **Merged pull requests:** 105 | 106 | - webmock: Allow 3.x [\#101](https://github.com/voxpupuli/puppet-blacksmith/pull/101) ([bastelfreak](https://github.com/bastelfreak)) 107 | - Implement GHA/GCG; Add badges to README.md [\#98](https://github.com/voxpupuli/puppet-blacksmith/pull/98) ([bastelfreak](https://github.com/bastelfreak)) 108 | 109 | ## [v6.1.0](https://github.com/voxpupuli/puppet-blacksmith/tree/v6.1.0) (2020-11-18) 110 | 111 | [Full Changelog](https://github.com/voxpupuli/puppet-blacksmith/compare/v6.0.1...v6.1.0) 112 | 113 | **Merged pull requests:** 114 | 115 | - Use puppet-modulebuilder ~\> 0.2 [\#94](https://github.com/voxpupuli/puppet-blacksmith/pull/94) ([ekohl](https://github.com/ekohl)) 116 | - Support API keys for the Puppet Forge [\#93](https://github.com/voxpupuli/puppet-blacksmith/pull/93) ([ekohl](https://github.com/ekohl)) 117 | - Add license to gemspec [\#91](https://github.com/voxpupuli/puppet-blacksmith/pull/91) ([timhughes](https://github.com/timhughes)) 118 | 119 | ## [v6.0.1](https://github.com/voxpupuli/puppet-blacksmith/tree/v6.0.1) (2020-10-06) 120 | 121 | * Add support for Puppet Forge API keys 122 | * Add the full license text 123 | * Require puppet-modulebuilder ~> 0.2 124 | 125 | ## 6.0.1 126 | 127 | * Drop old ruby code 128 | * We had some code for Ruby 2.0 in the Gemfile. Since we require Ruby 2.4, we can drop the old code 129 | * Forge upload should always use namespace 130 | 131 | There is an author field in metadata.json, but `pdk build` always uses 132 | the module's namespace when building the tarball. Therefore, Blacksmith 133 | should do the same and not even use the author field. 134 | 135 | ## 6.0.0 136 | 137 | This module moves from [puppetlabs_spec_helper](https://github.com/puppetlabs/puppetlabs_spec_helper)'s `build` rake task to its own `module:build` task which uses [puppet-modulebuilder](https://github.com/puppetlabs/puppet-modulebuilder). This has the benefit that it no longer needs the Puppet face (Puppet < 6) or the PDK (Puppet >= 6) and becomes standalone. 138 | 139 | * Use puppet-modulebuilder to build modules 140 | 141 | ## 5.1.0 142 | 143 | * Enable Artifactory access via API key. This provides an additional authentication mechanism besides a password 144 | * Remove outdated variable reference. This fixes a regression in Artifactory upload exception handling 145 | 146 | ## 5.0.0 147 | 148 | Note: The gemspec got updated to require at least Ruby 2.4 or newer. Older 149 | versions are end of life and unsupported. 150 | 151 | * Add note about clearing the rake task cache 152 | * Add support for Artifactory uploads 153 | * Update the maximum allows puppet versions 154 | * Update description for module:release task 155 | * Factor out uploading to separate functions 156 | * Handle RC versions while patching 157 | * Improve testing code 158 | 159 | ## 4.1.2 160 | 161 | * Fix an incorrect checksum on rubygems 162 | 163 | ## 4.1.1 164 | 165 | * Expose `tag_sign` option in the RakeTask 166 | 167 | ## 4.1.0 168 | 169 | * Allow creating signed tags 170 | 171 | ## 4.0.1 172 | 173 | This version is a fixup release since the 4.0.0 release broke the CI of a user which was still using Ruby 1.9.3. 174 | 175 | * Require ruby >= 2.0.0 in the gemspec 176 | 177 | ## 4.0.0 178 | 179 | This version drops Modulefile support. Upstream Puppet has removed the support from the code and we used that. It means we no longer use the Puppet gem which greatly reduces the profile of the module. 180 | 181 | * Add has_tag? and has_version_tag? methods 182 | * Make exec_git a private method 183 | * Support Ruby 2.2 - 2.4 184 | * Rip out Modulefile support 185 | * Allow using newer rest-client and webmock versions 186 | * Handle spaces in git paths 187 | * Allow creating annotated git commits 188 | * Make commit message configurable 189 | * Fix rake module release order 190 | * Drop ruby 1.9 support 191 | 192 | ## 3.4.0 193 | 194 | * Pin rest-client and webmock for ruby 1.9 support 195 | * Add 'version', 'version:next' and 'version:next:[patch|major|minor]' 196 | * Allow loading forge credentials from project dir 197 | * Add bump_commit commands for patch, minor, major and full 198 | * Allow setting credentials via env vars 199 | * Add bump:full rake task 200 | 201 | ## 3.3.1 202 | 203 | * `bump:*` runs twice if task is defined in `Rakefile` 204 | 205 | ## 3.3.0 206 | 207 | * Allow releasing without building nor pushing to the forge, just git tagging [:clean, :tag, :bump_commit], using `build = false` 208 | 209 | ## 3.2.0 210 | 211 | * Enable bumping semantic version elements with `module:bump:` plus `patch`, `minor`, `major` 212 | 213 | ## 3.1.0 214 | 215 | * Added feature to bump dependencies `module:dependency[modulename, version]` 216 | * Add support for custom tag patterns 217 | 218 | ## 3.0.3 219 | 220 | * [Issue #11](https://github.com/maestrodev/puppet-blacksmith/issues/11) Require json and yaml in forge.rb 221 | * [Issue #10](https://github.com/maestrodev/puppet-blacksmith/issues/10) Added support to git commit the version 222 | 223 | ## 3.0.2 224 | 225 | * [Issue #9](https://github.com/maestrodev/puppet-blacksmith/issues/9) Better error messages when pushing to the Forge fails 226 | 227 | ## 3.0.1 228 | 229 | * Fix uninitialized constant Blacksmith::VERSION 230 | 231 | ## 3.0.0 232 | 233 | * Use API to upload to the Forge instead of parsing the webpage 234 | * Use a custom Blacksmith `User-Agent` 235 | 236 | ## 2.3.1 237 | 238 | * [Issue #7](https://github.com/maestrodev/puppet-blacksmith/issues/7) `module:bump_commit` fails if `Modulefile` does not exist 239 | 240 | ## 2.2.0 241 | 242 | * [Issue #6](https://github.com/maestrodev/puppet-blacksmith/issues/6) Add support for `metadata.json` 243 | 244 | ## 2.1.0 245 | 246 | * Support Puppet 3.5+ 247 | 248 | ## 2.0.0 249 | 250 | * Reworked to be a library plus rake tasks so it an be reused 251 | * `.puppetforge.yml` `forge` entry is deprecated, use `url` 252 | 253 | 254 | \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* 255 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 MaestroDev 2 | 3 | Apache License 4 | Version 2.0, January 2004 5 | http://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, 12 | and distribution as defined by Sections 1 through 9 of this document. 13 | 14 | "Licensor" shall mean the copyright owner or entity authorized by 15 | the copyright owner that is granting the License. 16 | 17 | "Legal Entity" shall mean the union of the acting entity and all 18 | other entities that control, are controlled by, or are under common 19 | control with that entity. For the purposes of this definition, 20 | "control" means (i) the power, direct or indirect, to cause the 21 | direction or management of such entity, whether by contract or 22 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 23 | outstanding shares, or (iii) beneficial ownership of such entity. 24 | 25 | "You" (or "Your") shall mean an individual or Legal Entity 26 | exercising permissions granted by this License. 27 | 28 | "Source" form shall mean the preferred form for making modifications, 29 | including but not limited to software source code, documentation 30 | source, and configuration files. 31 | 32 | "Object" form shall mean any form resulting from mechanical 33 | transformation or translation of a Source form, including but 34 | not limited to compiled object code, generated documentation, 35 | and conversions to other media types. 36 | 37 | "Work" shall mean the work of authorship, whether in Source or 38 | Object form, made available under the License, as indicated by a 39 | copyright notice that is included in or attached to the work 40 | (an example is provided in the Appendix below). 41 | 42 | "Derivative Works" shall mean any work, whether in Source or Object 43 | form, that is based on (or derived from) the Work and for which the 44 | editorial revisions, annotations, elaborations, or other modifications 45 | represent, as a whole, an original work of authorship. For the purposes 46 | of this License, Derivative Works shall not include works that remain 47 | separable from, or merely link (or bind by name) to the interfaces of, 48 | the Work and Derivative Works thereof. 49 | 50 | "Contribution" shall mean any work of authorship, including 51 | the original version of the Work and any modifications or additions 52 | to that Work or Derivative Works thereof, that is intentionally 53 | submitted to Licensor for inclusion in the Work by the copyright owner 54 | or by an individual or Legal Entity authorized to submit on behalf of 55 | the copyright owner. For the purposes of this definition, "submitted" 56 | means any form of electronic, verbal, or written communication sent 57 | to the Licensor or its representatives, including but not limited to 58 | communication on electronic mailing lists, source code control systems, 59 | and issue tracking systems that are managed by, or on behalf of, the 60 | Licensor for the purpose of discussing and improving the Work, but 61 | excluding communication that is conspicuously marked or otherwise 62 | designated in writing by the copyright owner as "Not a Contribution." 63 | 64 | "Contributor" shall mean Licensor and any individual or Legal Entity 65 | on behalf of whom a Contribution has been received by Licensor and 66 | subsequently incorporated within the Work. 67 | 68 | 2. Grant of Copyright License. Subject to the terms and conditions of 69 | this License, each Contributor hereby grants to You a perpetual, 70 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 71 | copyright license to reproduce, prepare Derivative Works of, 72 | publicly display, publicly perform, sublicense, and distribute the 73 | Work and such Derivative Works in Source or Object form. 74 | 75 | 3. Grant of Patent License. Subject to the terms and conditions of 76 | this License, each Contributor hereby grants to You a perpetual, 77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 78 | (except as stated in this section) patent license to make, have made, 79 | use, offer to sell, sell, import, and otherwise transfer the Work, 80 | where such license applies only to those patent claims licensable 81 | by such Contributor that are necessarily infringed by their 82 | Contribution(s) alone or by combination of their Contribution(s) 83 | with the Work to which such Contribution(s) was submitted. If You 84 | institute patent litigation against any entity (including a 85 | cross-claim or counterclaim in a lawsuit) alleging that the Work 86 | or a Contribution incorporated within the Work constitutes direct 87 | or contributory patent infringement, then any patent licenses 88 | granted to You under this License for that Work shall terminate 89 | as of the date such litigation is filed. 90 | 91 | 4. Redistribution. You may reproduce and distribute copies of the 92 | Work or Derivative Works thereof in any medium, with or without 93 | modifications, and in Source or Object form, provided that You 94 | meet the following conditions: 95 | 96 | (a) You must give any other recipients of the Work or 97 | Derivative Works a copy of this License; and 98 | 99 | (b) You must cause any modified files to carry prominent notices 100 | stating that You changed the files; and 101 | 102 | (c) You must retain, in the Source form of any Derivative Works 103 | that You distribute, all copyright, patent, trademark, and 104 | attribution notices from the Source form of the Work, 105 | excluding those notices that do not pertain to any part of 106 | the Derivative Works; and 107 | 108 | (d) If the Work includes a "NOTICE" text file as part of its 109 | distribution, then any Derivative Works that You distribute must 110 | include a readable copy of the attribution notices contained 111 | within such NOTICE file, excluding those notices that do not 112 | pertain to any part of the Derivative Works, in at least one 113 | of the following places: within a NOTICE text file distributed 114 | as part of the Derivative Works; within the Source form or 115 | documentation, if provided along with the Derivative Works; or, 116 | within a display generated by the Derivative Works, if and 117 | wherever such third-party notices normally appear. The contents 118 | of the NOTICE file are for informational purposes only and 119 | do not modify the License. You may add Your own attribution 120 | notices within Derivative Works that You distribute, alongside 121 | or as an addendum to the NOTICE text from the Work, provided 122 | that such additional attribution notices cannot be construed 123 | as modifying the License. 124 | 125 | You may add Your own copyright statement to Your modifications and 126 | may provide additional or different license terms and conditions 127 | for use, reproduction, or distribution of Your modifications, or 128 | for any such Derivative Works as a whole, provided Your use, 129 | reproduction, and distribution of the Work otherwise complies with 130 | the conditions stated in this License. 131 | 132 | 5. Submission of Contributions. Unless You explicitly state otherwise, 133 | any Contribution intentionally submitted for inclusion in the Work 134 | by You to the Licensor shall be under the terms and conditions of 135 | this License, without any additional terms or conditions. 136 | Notwithstanding the above, nothing herein shall supersede or modify 137 | the terms of any separate license agreement you may have executed 138 | with Licensor regarding such Contributions. 139 | 140 | 6. Trademarks. This License does not grant permission to use the trade 141 | names, trademarks, service marks, or product names of the Licensor, 142 | except as required for reasonable and customary use in describing the 143 | origin of the Work and reproducing the content of the NOTICE file. 144 | 145 | 7. Disclaimer of Warranty. Unless required by applicable law or 146 | agreed to in writing, Licensor provides the Work (and each 147 | Contributor provides its Contributions) on an "AS IS" BASIS, 148 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 149 | implied, including, without limitation, any warranties or conditions 150 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 151 | PARTICULAR PURPOSE. You are solely responsible for determining the 152 | appropriateness of using or redistributing the Work and assume any 153 | risks associated with Your exercise of permissions under this License. 154 | 155 | 8. Limitation of Liability. In no event and under no legal theory, 156 | whether in tort (including negligence), contract, or otherwise, 157 | unless required by applicable law (such as deliberate and grossly 158 | negligent acts) or agreed to in writing, shall any Contributor be 159 | liable to You for damages, including any direct, indirect, special, 160 | incidental, or consequential damages of any character arising as a 161 | result of this License or out of the use or inability to use the 162 | Work (including but not limited to damages for loss of goodwill, 163 | work stoppage, computer failure or malfunction, or any and all 164 | other commercial damages or losses), even if such Contributor 165 | has been advised of the possibility of such damages. 166 | 167 | 9. Accepting Warranty or Additional Liability. While redistributing 168 | the Work or Derivative Works thereof, You may choose to offer, 169 | and charge a fee for, acceptance of support, warranty, indemnity, 170 | or other liability obligations and/or rights consistent with this 171 | License. However, in accepting such obligations, You may act only 172 | on Your own behalf and on Your sole responsibility, not on behalf 173 | of any other Contributor, and only if You agree to indemnify, 174 | defend, and hold each Contributor harmless for any liability 175 | incurred by, or claims asserted against, such Contributor by reason 176 | of your accepting any such warranty or additional liability. 177 | 178 | END OF TERMS AND CONDITIONS 179 | 180 | APPENDIX: How to apply the Apache License to your work. 181 | 182 | To apply the Apache License to your work, attach the following 183 | boilerplate notice, with the fields enclosed by brackets "[]" 184 | replaced with your own identifying information. (Don't include 185 | the brackets!) The text should be enclosed in the appropriate 186 | comment syntax for the file format. We also recommend that a 187 | file or class name and description of purpose be included on the 188 | same "printed page" as the copyright notice for easier 189 | identification within third-party archives. 190 | 191 | Copyright [yyyy] [name of copyright owner] 192 | 193 | Licensed under the Apache License, Version 2.0 (the "License"); 194 | you may not use this file except in compliance with the License. 195 | You may obtain a copy of the License at 196 | 197 | http://www.apache.org/licenses/LICENSE-2.0 198 | 199 | Unless required by applicable law or agreed to in writing, software 200 | distributed under the License is distributed on an "AS IS" BASIS, 201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 202 | See the License for the specific language governing permissions and 203 | limitations under the License. 204 | -------------------------------------------------------------------------------- /features/update_version.feature: -------------------------------------------------------------------------------- 1 | Feature: update_version 2 | puppet-blacksmith needs to update module versions 3 | 4 | Scenario: Bumping a module version when using metadata.json 5 | Given a file named "Rakefile" with: 6 | """ 7 | require "#{__dir__}/../../lib/puppet_blacksmith/rake_tasks" 8 | """ 9 | And a file named "metadata.json" with: 10 | """ 11 | { 12 | "operatingsystem_support": [ 13 | { 14 | "operatingsystem": "CentOS", 15 | "operatingsystemrelease": [ 16 | "4", 17 | "5", 18 | "6" 19 | ] 20 | } 21 | ], 22 | "requirements": [ 23 | { 24 | "name": "pe", 25 | "version_requirement": "3.2.x" 26 | }, 27 | { 28 | "name": "puppet", 29 | "version_requirement": ">=2.7.20 <7.0.0" 30 | } 31 | ], 32 | "name": "maestrodev-test", 33 | "version": "1.0.0", 34 | "source": "git://github.com/puppetlabs/puppetlabs-stdlib", 35 | "author": "maestrodev", 36 | "license": "Apache 2.0", 37 | "summary": "Puppet Module Standard Library", 38 | "description": "Standard Library for Puppet Modules", 39 | "project_page": "https://github.com/puppetlabs/puppetlabs-stdlib", 40 | "dependencies": [ 41 | ] 42 | } 43 | """ 44 | When I run `rake module:bump` 45 | Then the exit status should be 0 46 | And the file "metadata.json" should contain: 47 | """ 48 | { 49 | "operatingsystem_support": [ 50 | { 51 | "operatingsystem": "CentOS", 52 | "operatingsystemrelease": [ 53 | "4", 54 | "5", 55 | "6" 56 | ] 57 | } 58 | ], 59 | "requirements": [ 60 | { 61 | "name": "pe", 62 | "version_requirement": "3.2.x" 63 | }, 64 | { 65 | "name": "puppet", 66 | "version_requirement": ">=2.7.20 <7.0.0" 67 | } 68 | ], 69 | "name": "maestrodev-test", 70 | "version": "1.0.1", 71 | "source": "git://github.com/puppetlabs/puppetlabs-stdlib", 72 | "author": "maestrodev", 73 | "license": "Apache 2.0", 74 | "summary": "Puppet Module Standard Library", 75 | "description": "Standard Library for Puppet Modules", 76 | "project_page": "https://github.com/puppetlabs/puppetlabs-stdlib", 77 | """ 78 | 79 | 80 | Scenario: Bumping a module patch version when using metadata.json 81 | Given a file named "Rakefile" with: 82 | """ 83 | require "#{__dir__}/../../lib/puppet_blacksmith/rake_tasks" 84 | """ 85 | And a file named "metadata.json" with: 86 | """ 87 | { 88 | "operatingsystem_support": [ 89 | { 90 | "operatingsystem": "CentOS", 91 | "operatingsystemrelease": [ 92 | "4", 93 | "5", 94 | "6" 95 | ] 96 | } 97 | ], 98 | "requirements": [ 99 | { 100 | "name": "pe", 101 | "version_requirement": "3.2.x" 102 | }, 103 | { 104 | "name": "puppet", 105 | "version_requirement": ">=2.7.20 <4.0.0" 106 | } 107 | ], 108 | "name": "maestrodev-test", 109 | "version": "1.0.0", 110 | "source": "git://github.com/puppetlabs/puppetlabs-stdlib", 111 | "author": "maestrodev", 112 | "license": "Apache 2.0", 113 | "summary": "Puppet Module Standard Library", 114 | "description": "Standard Library for Puppet Modules", 115 | "project_page": "https://github.com/puppetlabs/puppetlabs-stdlib", 116 | "dependencies": [ 117 | ] 118 | } 119 | """ 120 | When I run `rake module:bump:patch` 121 | Then the exit status should be 0 122 | And the file "metadata.json" should contain: 123 | """ 124 | { 125 | "operatingsystem_support": [ 126 | { 127 | "operatingsystem": "CentOS", 128 | "operatingsystemrelease": [ 129 | "4", 130 | "5", 131 | "6" 132 | ] 133 | } 134 | ], 135 | "requirements": [ 136 | { 137 | "name": "pe", 138 | "version_requirement": "3.2.x" 139 | }, 140 | { 141 | "name": "puppet", 142 | "version_requirement": ">=2.7.20 <4.0.0" 143 | } 144 | ], 145 | "name": "maestrodev-test", 146 | "version": "1.0.1", 147 | "source": "git://github.com/puppetlabs/puppetlabs-stdlib", 148 | "author": "maestrodev", 149 | "license": "Apache 2.0", 150 | "summary": "Puppet Module Standard Library", 151 | "description": "Standard Library for Puppet Modules", 152 | "project_page": "https://github.com/puppetlabs/puppetlabs-stdlib", 153 | """ 154 | 155 | 156 | Scenario: Bumping a module minor version when using metadata.json 157 | Given a file named "Rakefile" with: 158 | """ 159 | require "#{__dir__}/../../lib/puppet_blacksmith/rake_tasks" 160 | """ 161 | And a file named "metadata.json" with: 162 | """ 163 | { 164 | "operatingsystem_support": [ 165 | { 166 | "operatingsystem": "CentOS", 167 | "operatingsystemrelease": [ 168 | "4", 169 | "5", 170 | "6" 171 | ] 172 | } 173 | ], 174 | "requirements": [ 175 | { 176 | "name": "pe", 177 | "version_requirement": "3.2.x" 178 | }, 179 | { 180 | "name": "puppet", 181 | "version_requirement": ">=2.7.20 <4.0.0" 182 | } 183 | ], 184 | "name": "maestrodev-test", 185 | "version": "1.0.0", 186 | "source": "git://github.com/puppetlabs/puppetlabs-stdlib", 187 | "author": "maestrodev", 188 | "license": "Apache 2.0", 189 | "summary": "Puppet Module Standard Library", 190 | "description": "Standard Library for Puppet Modules", 191 | "project_page": "https://github.com/puppetlabs/puppetlabs-stdlib", 192 | "dependencies": [ 193 | ] 194 | } 195 | """ 196 | When I run `rake module:bump:minor` 197 | Then the exit status should be 0 198 | And the file "metadata.json" should contain: 199 | """ 200 | { 201 | "operatingsystem_support": [ 202 | { 203 | "operatingsystem": "CentOS", 204 | "operatingsystemrelease": [ 205 | "4", 206 | "5", 207 | "6" 208 | ] 209 | } 210 | ], 211 | "requirements": [ 212 | { 213 | "name": "pe", 214 | "version_requirement": "3.2.x" 215 | }, 216 | { 217 | "name": "puppet", 218 | "version_requirement": ">=2.7.20 <4.0.0" 219 | } 220 | ], 221 | "name": "maestrodev-test", 222 | "version": "1.1.0", 223 | "source": "git://github.com/puppetlabs/puppetlabs-stdlib", 224 | "author": "maestrodev", 225 | "license": "Apache 2.0", 226 | "summary": "Puppet Module Standard Library", 227 | "description": "Standard Library for Puppet Modules", 228 | "project_page": "https://github.com/puppetlabs/puppetlabs-stdlib", 229 | """ 230 | 231 | 232 | Scenario: Bumping a module major version when using metadata.json 233 | Given a file named "Rakefile" with: 234 | """ 235 | require "#{__dir__}/../../lib/puppet_blacksmith/rake_tasks" 236 | """ 237 | And a file named "metadata.json" with: 238 | """ 239 | { 240 | "operatingsystem_support": [ 241 | { 242 | "operatingsystem": "CentOS", 243 | "operatingsystemrelease": [ 244 | "4", 245 | "5", 246 | "6" 247 | ] 248 | } 249 | ], 250 | "requirements": [ 251 | { 252 | "name": "pe", 253 | "version_requirement": "3.2.x" 254 | }, 255 | { 256 | "name": "puppet", 257 | "version_requirement": ">=2.7.20 <4.0.0" 258 | } 259 | ], 260 | "name": "maestrodev-test", 261 | "version": "1.0.0", 262 | "source": "git://github.com/puppetlabs/puppetlabs-stdlib", 263 | "author": "maestrodev", 264 | "license": "Apache 2.0", 265 | "summary": "Puppet Module Standard Library", 266 | "description": "Standard Library for Puppet Modules", 267 | "project_page": "https://github.com/puppetlabs/puppetlabs-stdlib", 268 | "dependencies": [ 269 | ] 270 | } 271 | """ 272 | When I run `rake module:bump:major` 273 | Then the exit status should be 0 274 | And the file "metadata.json" should contain: 275 | """ 276 | { 277 | "operatingsystem_support": [ 278 | { 279 | "operatingsystem": "CentOS", 280 | "operatingsystemrelease": [ 281 | "4", 282 | "5", 283 | "6" 284 | ] 285 | } 286 | ], 287 | "requirements": [ 288 | { 289 | "name": "pe", 290 | "version_requirement": "3.2.x" 291 | }, 292 | { 293 | "name": "puppet", 294 | "version_requirement": ">=2.7.20 <4.0.0" 295 | } 296 | ], 297 | "name": "maestrodev-test", 298 | "version": "2.0.0", 299 | "source": "git://github.com/puppetlabs/puppetlabs-stdlib", 300 | "author": "maestrodev", 301 | "license": "Apache 2.0", 302 | "summary": "Puppet Module Standard Library", 303 | "description": "Standard Library for Puppet Modules", 304 | "project_page": "https://github.com/puppetlabs/puppetlabs-stdlib", 305 | """ 306 | 307 | 308 | Scenario: Bumping to exact module version when using metadata.json 309 | Given a file named "Rakefile" with: 310 | """ 311 | require "#{__dir__}/../../lib/puppet_blacksmith/rake_tasks" 312 | """ 313 | And I set the environment variables to: 314 | | variable | value | 315 | | BLACKSMITH_FULL_VERSION | 2.1.3 | 316 | And a file named "metadata.json" with: 317 | """ 318 | { 319 | "operatingsystem_support": [ 320 | { 321 | "operatingsystem": "CentOS", 322 | "operatingsystemrelease": [ 323 | "4", 324 | "5", 325 | "6" 326 | ] 327 | } 328 | ], 329 | "requirements": [ 330 | { 331 | "name": "pe", 332 | "version_requirement": "3.2.x" 333 | }, 334 | { 335 | "name": "puppet", 336 | "version_requirement": ">=2.7.20 <4.0.0" 337 | } 338 | ], 339 | "name": "maestrodev-test", 340 | "version": "1.0.0", 341 | "source": "git://github.com/puppetlabs/puppetlabs-stdlib", 342 | "author": "maestrodev", 343 | "license": "Apache 2.0", 344 | "summary": "Puppet Module Standard Library", 345 | "description": "Standard Library for Puppet Modules", 346 | "project_page": "https://github.com/puppetlabs/puppetlabs-stdlib", 347 | "dependencies": [ 348 | ] 349 | } 350 | """ 351 | When I run `rake module:bump:full` 352 | Then the exit status should be 0 353 | And the file "metadata.json" should contain: 354 | """ 355 | { 356 | "operatingsystem_support": [ 357 | { 358 | "operatingsystem": "CentOS", 359 | "operatingsystemrelease": [ 360 | "4", 361 | "5", 362 | "6" 363 | ] 364 | } 365 | ], 366 | "requirements": [ 367 | { 368 | "name": "pe", 369 | "version_requirement": "3.2.x" 370 | }, 371 | { 372 | "name": "puppet", 373 | "version_requirement": ">=2.7.20 <4.0.0" 374 | } 375 | ], 376 | "name": "maestrodev-test", 377 | "version": "2.1.3", 378 | "source": "git://github.com/puppetlabs/puppetlabs-stdlib", 379 | "author": "maestrodev", 380 | "license": "Apache 2.0", 381 | "summary": "Puppet Module Standard Library", 382 | "description": "Standard Library for Puppet Modules", 383 | "project_page": "https://github.com/puppetlabs/puppetlabs-stdlib", 384 | """ 385 | 386 | 387 | Scenario: Bumping to exact module version but not setting the environment variable when using metadata.json 388 | Given a file named "Rakefile" with: 389 | """ 390 | require "#{__dir__}/../../lib/puppet_blacksmith/rake_tasks" 391 | """ 392 | And a file named "metadata.json" with: 393 | """ 394 | { 395 | "operatingsystem_support": [ 396 | { 397 | "operatingsystem": "CentOS", 398 | "operatingsystemrelease": [ 399 | "4", 400 | "5", 401 | "6" 402 | ] 403 | } 404 | ], 405 | "requirements": [ 406 | { 407 | "name": "pe", 408 | "version_requirement": "3.2.x" 409 | }, 410 | { 411 | "name": "puppet", 412 | "version_requirement": ">=2.7.20 <4.0.0" 413 | } 414 | ], 415 | "name": "maestrodev-test", 416 | "version": "1.0.0", 417 | "source": "git://github.com/puppetlabs/puppetlabs-stdlib", 418 | "author": "maestrodev", 419 | "license": "Apache 2.0", 420 | "summary": "Puppet Module Standard Library", 421 | "description": "Standard Library for Puppet Modules", 422 | "project_page": "https://github.com/puppetlabs/puppetlabs-stdlib", 423 | "dependencies": [ 424 | ] 425 | } 426 | """ 427 | When I run `rake module:bump:full` 428 | Then the exit status should be 1 429 | And the output should contain "Setting the full version requires setting the " 430 | And the file "metadata.json" should contain: 431 | """ 432 | { 433 | "operatingsystem_support": [ 434 | { 435 | "operatingsystem": "CentOS", 436 | "operatingsystemrelease": [ 437 | "4", 438 | "5", 439 | "6" 440 | ] 441 | } 442 | ], 443 | "requirements": [ 444 | { 445 | "name": "pe", 446 | "version_requirement": "3.2.x" 447 | }, 448 | { 449 | "name": "puppet", 450 | "version_requirement": ">=2.7.20 <4.0.0" 451 | } 452 | ], 453 | "name": "maestrodev-test", 454 | "version": "1.0.0", 455 | "source": "git://github.com/puppetlabs/puppetlabs-stdlib", 456 | "author": "maestrodev", 457 | "license": "Apache 2.0", 458 | "summary": "Puppet Module Standard Library", 459 | "description": "Standard Library for Puppet Modules", 460 | "project_page": "https://github.com/puppetlabs/puppetlabs-stdlib", 461 | """ 462 | --------------------------------------------------------------------------------