diff --git a/.github/workflows/gem_release.yaml b/.github/workflows/gem_release.yaml new file mode 100644 index 000000000..415f316d2 --- /dev/null +++ b/.github/workflows/gem_release.yaml @@ -0,0 +1,41 @@ +--- +name: Gem Release + +on: + push: + tags: + - '*' + +jobs: + release: + name: Release gem + runs-on: ubuntu-24.04 + # Optional but recommended to use a specific environment + environment: release + # Prevent releases from forked repositories + if: github.repository_owner == 'OpenVoxProject' + + permissions: + id-token: write + contents: write + packages: write + + steps: + - uses: voxpupuli/ruby-release@v0 + - name: Setup GitHub packages access + run: | + mkdir -p ~/.gem + echo ":github: Bearer ${{ secrets.GITHUB_TOKEN }}" >> ~/.gem/credentials + chmod 0600 ~/.gem/credentials + - name: Publish gem to GitHub packages + run: gem push --key github --host https://rubygems.pkg.github.com/openvoxproject *.gem + - name: Create Release Page + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: gh release create ${{ github.ref_name }} --generate-notes + - name: Attach gem to GitHub Release + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: gh release upload ${{ github.ref_name }} *.gem diff --git a/.github/workflows/prepare_release.yml b/.github/workflows/prepare_release.yml new file mode 100644 index 000000000..811c5d75b --- /dev/null +++ b/.github/workflows/prepare_release.yml @@ -0,0 +1,28 @@ +name: 'Prepare Release' + +on: + workflow_dispatch: + inputs: + version: + description: 'Version to be released.' + required: false + default: '' + type: string + base-branch: + description: 'The branch that will be used as the origin for the release branch.' + required: false + default: '' + type: string + +permissions: {} + +jobs: + prepare_release: + uses: OpenVoxProject/shared-actions/.github/workflows/prepare_release.yml@main + with: + allowed_owner: 'OpenVoxProject' + base-branch: ${{ github.event.inputs.base-branch }} + version: ${{ github.event.inputs.version }} + secrets: + github_pat: ${{ secrets.OPENVOXBOT_COMMIT_AND_PRS }} + ssh_private_key: ${{ secrets.OPENVOXBOT_SSH_PRIVATE_KEY }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b54c28ebe..d2b9219aa 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,49 +1,28 @@ ---- -name: Gem Release +name: 'Release' on: - push: - tags: - - '*' + workflow_dispatch: + inputs: + version: + description: 'Version to be released.' + required: false + default: '' + type: string + base-branch: + description: 'The branch where we do this release.' + required: false + default: '' + type: string + +permissions: {} jobs: release: - name: Release gem - runs-on: ubuntu-24.04 - # Optional but recommended to use a specific environment - environment: release - # Prevent releases from forked repositories - if: github.repository_owner == 'OpenVoxProject' - - permissions: - id-token: write - contents: write - packages: write - - steps: - # we cannot publish to rubygems.org until we rename the project - # https://rubygems.org/gems/vanagon owned by Perforce - # - uses: voxpupuli/ruby-release@v0 - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Install Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: '3.4' - env: - BUNDLE_WITHOUT: integration:documentation:development:test - - name: Build gem - run: gem build --strict --verbose *.gemspec - - name: Setup GitHub packages access - run: | - mkdir -p ~/.gem - echo ":github: Bearer ${{ secrets.GITHUB_TOKEN }}" >> ~/.gem/credentials - chmod 0600 ~/.gem/credentials - - name: Publish gem to GitHub packages - run: gem push --key github --host https://rubygems.pkg.github.com/openvoxproject *.gem - - name: Create Release Page - shell: bash - env: - GH_TOKEN: ${{ github.token }} - run: gh release create ${{ github.ref_name }} --generate-notes + uses: OpenVoxProject/shared-actions/.github/workflows/release.yml@main + with: + allowed_owner: 'OpenVoxProject' + base-branch: ${{ github.event.inputs.base-branch }} + version: ${{ github.event.inputs.version }} + secrets: + github_pat: ${{ secrets.OPENVOXBOT_COMMIT_AND_PRS }} + ssh_private_key: ${{ secrets.OPENVOXBOT_SSH_PRIVATE_KEY }} diff --git a/Gemfile b/Gemfile index 70f70843e..4779651d9 100644 --- a/Gemfile +++ b/Gemfile @@ -2,10 +2,10 @@ source ENV['GEM_SOURCE'] || 'https://rubygems.org' -gemspec name: 'facter' +gemspec group(:release, optional: true) do - gem 'octokit', '~> 4.18.0' + gem 'github_changelog_generator' end gem 'packaging', require: false diff --git a/CHANGELOG.md b/HISTORY.md similarity index 99% rename from CHANGELOG.md rename to HISTORY.md index 2d2e20cba..9d1d78f20 100644 --- a/CHANGELOG.md +++ b/HISTORY.md @@ -1,4 +1,6 @@ -## Facter release notes are now provided as part of the [official Puppet documentation](https://puppet.com/docs/puppet/7/release_notes_facter.html) and are no longer tracked in this file. +## 4.10.0 + +After 4.0.44, Facter release notes were provided as part of the [official Puppet documentation](https://puppet.com/docs/puppet/7/release_notes_facter.html) and were not tracked in this file. # Previous versions diff --git a/README.md b/README.md index e7cd1d605..2f47788c0 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,8 @@ -# facter +# openfact -[![Gem Version](https://badge.fury.io/rb/facter.svg)](https://badge.fury.io/rb/facter) -[](https://puppetcommunity.slack.com/messages/C0W1X7ZAL) +[![Gem Version](https://badge.fury.io/rb/openfact.svg)](https://badge.fury.io/rb/openfact) -[![Modules Status](https://github.com/puppetlabs/facter/workflows/Acceptance%20tests/badge.svg?branch=main)](https://github.com/puppetlabs/facter/actions) -[![Modules Status](https://github.com/puppetlabs/facter/workflows/Unit%20tests/badge.svg?branch=main)](https://github.com/puppetlabs/facter/actions) -[![Modules Status](https://github.com/puppetlabs/facter/workflows/Checks/badge.svg?branch=main)](https://github.com/puppetlabs/facter/actions) - -Facter is a command-line tool that gathers basic facts about nodes (systems) +OpenFact is a community implementation of [Facter](https://github.com/puppetlabs/facter/), a command-line tool that gathers basic facts about nodes (systems) such as hardware details, network settings, OS type and version, and more. These facts are made available as variables in your Puppet manifests and can be used to inform conditional expressions in Puppet. @@ -17,6 +12,10 @@ used to inform conditional expressions in Puppet. Documentation for the Facter project can be found on the [Puppet Docs site](https://puppet.com/docs/puppet/latest/facter.html). +At the time of writing, the OpenVoxProject does not have a documentation website. +But your help is very welcome! +Reach out to the [Documentation Special Interest Group](https://github.com/voxpupuli/community-triage/wiki/SIG.Documentation) if you want to help. + ## Supported platforms * Linux * macOS @@ -32,7 +31,7 @@ site](https://puppet.com/docs/puppet/latest/facter.html). The project has three main parts, the framework, facts and resolvers. In the framework we implement functionality that is agnostic of specific facts like parsing user input, formatting output, etc. -Facts are the nuggets of information that will be provided by facter e.g. `os.name`, `networking.interfaces`, etc. +Facts are the nuggets of information that will be provided by openfact e.g. `os.name`, `networking.interfaces`, etc. Resolvers have the role of gathering data from the system. For example a resolver can execute a command on the system, can read a file or any operation that retrieves some data from a single source on the system. @@ -54,17 +53,14 @@ sequenceDiagram ## Getting started After cloning the project, run `bundle install` to install all dependencies. -You can run facter by executing `./bin/facter`. -The command will output all the facts that facter detected for the current OS. +You can run openfact by executing `./bin/facter`. +The command will output all the facts that openfact detected for the current OS. The implementation can be validated locally by running `bundle exec rake check`. -## Goals - fast, easy, compatible -* Gain performance similar to the C++ version of Facter. We plan to achieve this goal by gathering multiple facts with only one call and by using the faster Win32 API rather than WMI for the Windows implementation. -* Facilitate community contribution. At the moment, C++ presents a possible impediment for community contributions. -* Enable native integration with other Ruby-based projects such as Bolt and puppet. -* Enable native integration for custom facts. -* Provide 100% compatibility with C++ Facter (drop-in replacement). - ## Licensing -See [LICENSE](https://github.com/puppetlabs/facter/blob/main/LICENSE) file. Puppet is licensed by Puppet, Inc. under the Apache license. Puppet, Inc. can be contacted at: info@puppet.com +See [LICENSE](LICENSE) file. +OpenFact is licensed by the OpenVox Project as a community maintained implementation of Facter. +The OpenVox Project can be contacted at: openvox@voxpupuli.org. +Puppet itself is licensed by Puppet, Inc. under the Apache license. +Puppet, Inc. can be contacted at: info@puppet.com. diff --git a/Rakefile b/Rakefile index 055f62d1b..437735e18 100644 --- a/Rakefile +++ b/Rakefile @@ -9,9 +9,25 @@ Dir.glob(File.join('tasks/**/*.rake')).each { |file| load file } task default: :spec -desc 'Generate changelog' -task :changelog, [:version] do |_t, args| - sh "./scripts/generate_changelog.rb #{args[:version]}" +begin + require 'github_changelog_generator/task' + require_relative 'lib/facter/version' + + GitHubChangelogGenerator::RakeTask.new :changelog do |config| + config.header = <<~HEADER.chomp + # Changelog + + All notable changes to this project will be documented in this file. + HEADER + config.user = 'openvoxproject' + config.project = 'openfact' + config.exclude_labels = %w[dependencies duplicate question invalid wontfix wont-fix modulesync skip-changelog] + config.future_release = Facter::VERSION + end +rescue LoadError + task :changelog do + abort('Run `bundle install --with release` to install the `github_changelog_generator` gem.') + end end namespace :pl_ci do diff --git a/facter.gemspec b/openfact.gemspec similarity index 89% rename from facter.gemspec rename to openfact.gemspec index 74ed30af9..68c1c0ea7 100644 --- a/facter.gemspec +++ b/openfact.gemspec @@ -4,13 +4,13 @@ lib = File.expand_path('lib', __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) Gem::Specification.new do |spec| - spec.name = 'facter' + spec.name = 'openfact' spec.version = '4.11.0' - spec.authors = ['Puppet'] - spec.email = ['team-nw@puppet.com'] - spec.homepage = 'https://github.com/puppetlabs/facter' + spec.authors = ['OpenVox Project'] + spec.email = ['openvox@voxpupuli.org'] + spec.homepage = 'https://github.com/OpenVoxProject/openfact/' - spec.summary = 'Facter, a system inventory tool' + spec.summary = 'OpenFact, a system inventory tool' spec.description = 'You can prove anything with facts!' spec.license = 'Apache-2.0' diff --git a/scripts/generate_changelog.rb b/scripts/generate_changelog.rb deleted file mode 100755 index db7f9c12c..000000000 --- a/scripts/generate_changelog.rb +++ /dev/null @@ -1,158 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -require 'octokit' - -class ChangelogGenerator - attr_reader :version, :entries, :unlabeled_prs - - def initialize(version) - unless version - warn 'Usage: generate_changelog.rb VERSION' - exit 1 - end - - @version = version - @entries = { - 'feature' => { name: 'Added', entries: {} }, - 'bugfix' => { name: 'Fixed', entries: {} }, - 'backwards-incompatible' => { name: 'Changed', entries: {} } - } - - @unlabeled_prs = [] - - # Setting the changelog path early lets us check that it exists - # before we spend time making API calls - changelog - end - - def labels - @entries.keys - end - - def client - unless @client - unless ENV['GITHUB_TOKEN'] - warn 'Missing GitHub personal access token. Set $GITHUB_TOKEN with a '\ - 'personal access token to use this script.' - exit 1 - end - - Octokit.configure do |c| - c.auto_paginate = true - end - - @client = Octokit::Client.new(access_token: ENV['GITHUB_TOKEN']) - end - - @client - end - - def latest - @latest ||= client.latest_release('puppetlabs/facter').tag_name - end - - def commits - @commits ||= client.compare('puppetlabs/facter', latest, 'main').commits - end - - def changelog - unless @changelog - @changelog = File.expand_path('CHANGELOG.md', Dir.pwd) - - unless File.file?(@changelog) - warn "Unable to find changelog at #{@changelog}" - exit 1 - end - end - - @changelog - end - - # Parses individual commits by scanning the commit message for valid release notes - # and adding them to the list of entries. Entries include extra information about - # the author and whether it was an internal or external contribution so we can give - # kudos. - def parse_commit(commit) - prs = client.commit_pulls('puppetlabs/facter', commit.sha, { accept: 'application/vnd.github.groot-preview+json' }) - - prs.each do |pr| - next if pr[:state] != 'closed' && pr[:merged_at].nil? - - if (pr[:labels].nil? || pr[:labels].empty?) && !unlabeled_prs.include?(pr[:html_url]) - unlabeled_prs << pr[:html_url] - end - - pr[:labels].each do |label| - next unless entries.key?(label[:name]) - - entries[label[:name]][:entries][pr[:html_url]] = { - title: pr[:title], - number: pr[:number], - url: pr[:html_url], - author: pr[:user][:login], - profile: pr[:user][:html_url] - } - end - end - end - - def update_changelog - old_lines = File.read(changelog).split("\n") - - new_lines = [ - "## [#{version}](https://github.com/puppetlabs/facter/tree/#{version}) (#{Time.now.strftime '%Y-%m-%d'})\n", - "[Full Changelog](https://github.com/puppetlabs/facter/compare/#{latest}...#{version})" - ] - - entries.each_value do |type| - next unless type[:entries].any? - - new_lines << "\n### #{type[:name]}\n" - - type[:entries].each_value do |entry| - new_lines << "- #{entry[:title].strip} [\##{entry[:number]}](#{entry[:url]})" \ - " ([#{entry[:author]}](#{entry[:profile]}))" - end - end - - new_lines = check_unlabeled_prs(new_lines) - - content = (new_lines + ["\n"] + old_lines).join("\n") - - if File.write(changelog, content) - puts "Successfully wrote entries to #{changelog}" - else - warn "Unable to write entries to #{changelog}" - exit 1 - end - end - - def check_unlabeled_prs(content) - return content unless unlabeled_prs.any? - - content << "\n### Unlabeled PRs:\n" - unlabeled_prs.each do |pr| - content << "- #{pr}" - end - - content - end - - def generate - puts "Loading and parsing commits for #{latest}..main" - - commits.each do |commit| - parse_commit(commit) - end - - if entries.each_value.all? { |type| type[:entries].empty? } - warn "No release notes for #{latest}..main" - exit 0 - end - - update_changelog - end -end - -ChangelogGenerator.new(ARGV.first).generate if $PROGRAM_NAME == __FILE__ diff --git a/spec/facter/version_spec.rb b/spec/facter/version_spec.rb index b380600b6..04164229d 100644 --- a/spec/facter/version_spec.rb +++ b/spec/facter/version_spec.rb @@ -3,7 +3,7 @@ describe Facter do describe '#reported and gemspec files version' do it 'checks that reported and facter.gemspec versions are the same' do - gemspec_file_path = File.join(File.dirname(__FILE__), '..', '..', 'facter.gemspec') + gemspec_file_path = File.join(File.dirname(__FILE__), '..', '..', 'openfact.gemspec') gemspec_facter_version = Gem::Specification.load(gemspec_file_path).version.to_s expect(gemspec_facter_version).to eq(Facter::VERSION) diff --git a/tasks/vox.rake b/tasks/vox.rake new file mode 100644 index 000000000..3749fee3a --- /dev/null +++ b/tasks/vox.rake @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +namespace :vox do + desc 'Update the version in preparation for a release' + task 'version:bump:full', [:version] do |_, args| + abort 'You must provide a tag.' if args[:version].nil? || args[:version].empty? + version = args[:version] + abort "#{version} does not appear to be a valid version string in x.y.z format" unless Gem::Version.correct?(version) + + # Update lib/facter/version.rb and openvox.gemspec + puts "Setting version to #{version}" + + data = File.read('lib/facter/version.rb') + new_data = data.sub(/VERSION = '\d+\.\d+\.\d+'/, "VERSION = '#{version}'") + raise 'Failed to update version in lib/facter/version.rb' if data == new_data + + File.write('lib/facter/version.rb', new_data) + + data = File.read('openfact.gemspec') + new_data = data.sub(/(spec.version *=) '\d+\.\d+\.\d+'/, "\\1 '#{version}'") + raise 'Failed to update version in openfact.gemspec' if data == new_data + + File.write('openfact.gemspec', new_data) + end +end