Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
6daa61a
add commercial api support
Stromweld Jan 1, 2026
76b8ae4
update specs and add trial/free api urls
Stromweld Jan 5, 2026
2c23a15
adding json support for new api response
Stromweld Jan 5, 2026
8b63b3a
fix download to use content distrobution headers
Stromweld Jan 5, 2026
7dd6f88
test
Stromweld Jan 5, 2026
e604126
add chef-ice to product matrix
Stromweld Jan 6, 2026
fdd8a19
fixed unit tests
Stromweld Jan 6, 2026
68d8365
test
Stromweld Jan 6, 2026
17746da
fix windows installs
Stromweld Jan 6, 2026
0538fe7
linting fixes
Stromweld Jan 6, 2026
17e7c74
linting
Stromweld Jan 6, 2026
8f8ccc8
update integration tests to use add licensed testing
Stromweld Jan 6, 2026
0e1fa05
fix cli to work with licensed downloads
Stromweld Jan 7, 2026
cb084e6
linting fixes
Stromweld Jan 7, 2026
f1a141e
fix spec tests
Stromweld Jan 7, 2026
7036142
remove ostruct dep
Stromweld Jan 7, 2026
3cf8524
fix client download file name issues with trial api
Stromweld Jan 8, 2026
069f89b
linting fix
Stromweld Jan 8, 2026
05a9e03
update copilot-instructions for latest commercial-api changes
Stromweld Jan 8, 2026
48532a6
remove test script
Stromweld Jan 8, 2026
3162a5f
Add ADR record
Stromweld Jan 8, 2026
97f4dcf
linting fixes
Stromweld Jan 9, 2026
d99fe55
fix powershell filename when downloading chef-client using commercial…
Stromweld Jan 9, 2026
9c3ec1d
add review suggested change
Stromweld Jan 12, 2026
88cd7fc
updated rubocop target ruby version to 2.6
Stromweld Jan 12, 2026
cc486c1
chefstyle linting fixes
Stromweld Jan 12, 2026
f96b30d
Merge branch 'main' into commercial-api
Stromweld Jan 13, 2026
026a7cb
update copilot-instructions to use default numbered listing vs increm…
Stromweld Jan 13, 2026
4df9ff4
add server 2025 to powershell helpers
Stromweld Jan 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 136 additions & 57 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,43 @@ Mixlib::Install is a library for interacting with Chef Software Inc's software d
## Ruby Version Support Strategy

### Supported Ruby Versions
- **Minimum**: Ruby 2.3+
- **Target Range**: Ruby 2.3 through Ruby 3.4+
- **Testing Focus**: Maintain backward compatibility with Ruby 2.3+ while supporting latest Ruby releases
- **Minimum**: Ruby 2.6+
- **Target Range**: Ruby 2.6 through Ruby 3.4+
- **Testing Focus**: Maintain backward compatibility with Ruby 2.6+ while supporting latest Ruby releases

### Critical Compatibility Rules

1. **Avoid Modern Ruby Syntax**
- NO numbered parameters `_1, _2` (Ruby 2.7+)
- NO pattern matching (Ruby 2.7+)
- NO endless methods (Ruby 3.0+)
- Use Ruby 2.3-compatible syntax as the baseline
- Use Ruby 2.6-compatible syntax as the baseline

2. **Dependency Version Constraints**
1. **Dependency Version Constraints**
- Always use version-conditional dependency constraints in gemspec
- Follow the existing pattern for Ruby version-specific dependencies (see `openssl` gem constraints in gemspec)
- Consider backward compatibility when adding new dependencies
- Check Gemfile for Ruby version-specific gem constraints before adding dependencies

3. **Standard Library Compatibility**
1. **Standard Library Compatibility**
- Be cautious with stdlib changes across Ruby versions
- Test with methods available in Ruby 2.3
- Avoid relying on gems that dropped support for Ruby 2.3+
- Ruby 2.3 features that are safe to use:
- Test with methods available in Ruby 2.6
- Avoid relying on gems that dropped support for Ruby 2.6+
- Ruby 2.6 features that are safe to use:
- Safe navigation operator (`&.`)
- Squiggly heredoc (`<<~`)
- `dig` method on Hash and Array
- `grep_v` on Enumerable
- Frozen string literal comment
- Endless ranges: `(1..)`
- `Enumerable#chain`
- `Kernel#then`

## Code Style & Conventions

### RuboCop Configuration
- TargetRubyVersion: 2.7 (set in `.rubocop.yml`)
- Note: While RuboCop targets 2.7, code must remain compatible with Ruby 2.3+
- TargetRubyVersion: 2.6 (set in `.rubocop.yml`)
- Note: RuboCop targets 2.6 to match the minimum supported Ruby version
- Uses `chefstyle` gem version ~> 0.4.0
- Run style checks: `bundle exec rake style`

Expand Down Expand Up @@ -73,26 +76,33 @@ All Ruby files should include the Apache 2.0 license header:
- Provides `artifact_info`, `available_versions`, `install_command`, `download_artifact` methods
- Delegates to Backend for API interactions

2. **Options** (`lib/mixlib/install/options.rb`)
1. **Options** (`lib/mixlib/install/options.rb`)
- Validates and normalizes user input
- Supports EXTRA_PRODUCTS_FILE environment variable for custom products
- Key options: channel, product_name, product_version, platform, platform_version, architecture
- Key options: channel, product_name, product_version, platform, platform_version, architecture, license_id
- **license_id**: Enables commercial/trial API access for licensed Chef products

3. **Product Matrix** (`lib/mixlib/install/product_matrix.rb`)
1. **Product Matrix** (`lib/mixlib/install/product_matrix.rb`)
- DSL for defining product metadata
- Extensible via EXTRA_PRODUCTS_FILE
- Run `bundle exec rake matrix` to update PRODUCT_MATRIX.md after changes

4. **Backend** (`lib/mixlib/install/backend/`)
1. **Backend** (`lib/mixlib/install/backend/`)
- Package Router backend for Chef's package API
- Handles API communication with packages.chef.io

5. **Generators** (`lib/mixlib/install/generator/`)
- Bourne shell (install.sh) generator
- PowerShell (install.ps1) generator
- Supports proxy configuration and download_url_override

6. **Artifact Info** (`lib/mixlib/install/artifact_info.rb`)
1. **Generators** (`lib/mixlib/install/generator/`)
- Bourne shell (install.sh) generator with Content-Disposition header support
- PowerShell (install.ps1) generator with JSON API response parsing
- Supports proxy configuration, download_url_override, and license_id
- **Commercial/Trial API Support**: When license_id is provided, uses specialized download endpoints
- Trial API: `https://chefdownload-trial.chef.io` (for license IDs starting with `free-` or `trial-`)
- Commercial API: `https://chefdownload-commercial.chef.io` (for other license IDs)
- Returns JSON responses instead of text format
- Uses Content-Disposition headers for filename extraction
- Implements temp file download approach with multiple filename extraction methods

1. **Artifact Info** (`lib/mixlib/install/artifact_info.rb`)
- Represents package metadata
- Includes platform, version, URL, checksum, license info

Expand Down Expand Up @@ -140,12 +150,12 @@ When adding test dependencies, follow this pattern.
- Run `bundle exec rake matrix` to update documentation
- Add tests in `spec/unit/mixlib/install/product_spec.rb`

2. **Platform Support**
1. **Platform Support**
- Update `lib/mixlib/install/options.rb` SUPPORTED_ARCHITECTURES if needed
- Add platform detection logic in `lib/mixlib/install/util.rb`
- Update install script generators if platform-specific logic needed

3. **API Changes**
1. **API Changes**
- Maintain backward compatibility
- Add deprecation warnings before removing features
- Update README.md with examples
Expand All @@ -162,8 +172,8 @@ When adding test dependencies, follow this pattern.

#### Adding Dependencies to Gemspec
1. Consider minimum Ruby version compatibility
2. Use version constraints with Ruby version conditionals if needed
3. Example pattern (from gemspec):
1. Use version constraints with Ruby version conditionals if needed
1. Example pattern (from gemspec):
```ruby
if RUBY_VERSION < "2.7.0"
spec.add_dependency "openssl", ">= 3.1.2", "< 3.2.0"
Expand Down Expand Up @@ -210,17 +220,31 @@ The library includes sophisticated platform version compatibility logic:
- Supports: http_proxy, https_proxy, ftp_proxy, no_proxy
- Platform detection for Linux/Unix systems
- Generated via `lib/mixlib/install/generator/bourne.rb`
- **Content-Disposition Support**: When `license_id` is provided:
- Downloads to temp file: `chef-download-temp.$$`
- Extracts filename from HTTP response headers (3 methods):
1. Content-Disposition header: `attachment; filename="..."`
1. Location redirect header: Extract from redirect URL
1. URL pattern matching: Search for `.rpm|.deb|.pkg|.msi|.dmg` patterns
- Fallback: Constructs filename from platform metadata if extraction fails
- Renames temp file to extracted/constructed filename
- Works with all download methods: wget, curl, fetch, perl, python

### PowerShell (install.ps1)
- Supports: http_proxy
- Windows platform support
- TLS negotiation for older .NET versions
- Generated via `lib/mixlib/install/generator/powershell.rb`
- **JSON API Response**: When `license_id` is provided:
- Parses JSON responses with `ConvertFrom-Json`
- Extracts `url` and `sha256` from JSON object
- Automatically routes to trial or commercial API based on license_id prefix

### Script Options
- `download_url_override`: Direct URL instead of API lookup
- `checksum`: SHA256 for verification
- `install_strategy`: "once" to skip if already installed
- `license_id`: License ID for commercial/trial API access (format: `free-*`, `trial-*`, or standard license ID)

## API Usage Patterns

Expand Down Expand Up @@ -262,24 +286,85 @@ When implementing features, ensure this extensibility is maintained.
- Linux tests: `.expeditor/run_linux_tests.sh`
- Windows tests: `.expeditor/run_windows_tests.ps1`

## Commercial and Trial API Integration

### Overview
Mixlib::Install supports Chef's commercial and trial licensing APIs, which provide authenticated access to Chef products for licensed customers.

### API Endpoints
- **Trial API**: `https://chefdownload-trial.chef.io`
- Used when `license_id` starts with `free-` or `trial-`
- Returns JSON responses with download URLs
- **Commercial API**: `https://chefdownload-commercial.chef.io`
- Used for standard license IDs
- Returns JSON responses with download URLs
- **Traditional Omnitruck**: `https://omnitruck.chef.io`
- Used when no `license_id` is provided
- Returns text-based metadata responses

### Response Format Differences
- **Commercial/Trial APIs**: JSON format
```json
{
"url": "https://...",
"sha256": "abc123..."
}
```
- **Omnitruck API**: Text format
```
url\thttp://...
sha256\tabc123...
```

### Content-Disposition Header Handling
Commercial and trial APIs return endpoint URLs that use HTTP Content-Disposition headers to specify the actual filename, rather than including the filename in the URL path.

**Implementation Details**:
1. **Detection**: `use_content_disposition="true"` when `license_id` is present
1. **Download Strategy**: Use temp file with process ID suffix: `chef-download-temp.$$`
1. **Filename Extraction** (3 methods, attempted in order):
- Parse `Content-Disposition` header: `filename="chef-18.8.54-1.el9.x86_64.rpm"`
- Parse `Location` redirect header: Extract filename from redirect URL
- Pattern matching: Search stderr output for `.rpm|.deb|.pkg|.msi|.dmg` extensions
1. **Fallback Construction**: Build filename from platform metadata if extraction fails
1. **File Rename**: Move temp file to final location with extracted/constructed filename

**Cross-Platform Compatibility**: This approach works with all download methods:
- `wget` (with `--content-disposition` flag as secondary approach)
- `curl` (with `-O -J` flags as secondary approach)
- `fetch` (FreeBSD)
- `perl` (LWP::Simple)
- `python` (urllib2)

### Testing Commercial/Trial API Features
When adding or modifying commercial/trial API functionality:
1. Test with `license_id` starting with `free-` (trial API)
1. Test with `license_id` starting with `trial-` (trial API)
1. Test with standard license ID format (commercial API)
1. Verify JSON parsing in both Bourne shell (sed) and PowerShell (ConvertFrom-Json)
1. Test filename extraction with various response header formats
1. Verify fallback filename construction for each platform type

## Common Pitfalls to Avoid

1. **Don't use Ruby 2.4+ features** - Always consider Ruby 2.3 compatibility
2. **Don't assume gem availability** - Check version constraints in Gemfile first
3. **Don't break the Product Matrix DSL** - It's critical for product definitions
4. **Don't skip `rake matrix`** - Must run after modifying product_matrix.rb
5. **Don't hardcode URLs** - Use product definitions and API lookups
6. **Don't ignore platform compatibility** - Test across platforms when possible
7. **Don't add dependencies without version constraints** - Especially for Ruby 2.3+ support
1. **Don't use Ruby 2.7+ features** - Always consider Ruby 2.6 compatibility
1. **Don't assume gem availability** - Check version constraints in Gemfile first
1. **Don't break the Product Matrix DSL** - It's critical for product definitions
1. **Don't skip `rake matrix`** - Must run after modifying product_matrix.rb
1. **Don't hardcode URLs** - Use product definitions and API lookups
1. **Don't ignore platform compatibility** - Test across platforms when possible
1. **Don't add dependencies without version constraints** - Especially for Ruby 2.6+ support
1. **Don't assume filename in URL** - Commercial/trial APIs use Content-Disposition headers
1. **Don't break temp file download approach** - Required for license_id support across all download methods

## Documentation Requirements

When making changes:
1. Update README.md with API examples if public interface changes
2. Update CHANGELOG.md (handled by Expeditor)
3. Run `rake matrix` if products changed
4. Add code comments for complex compatibility logic
5. Document Ruby version requirements for new features
1. Update CHANGELOG.md (handled by Expeditor)
1. Run `rake matrix` if products changed
1. Add code comments for complex compatibility logic
1. Document Ruby version requirements for new features

## Performance Considerations

Expand All @@ -291,18 +376,18 @@ When making changes:
## Security Considerations

1. **Checksum Verification**: Always provide/verify SHA256 checksums
2. **HTTPS**: Use secure connections to packages.chef.io
3. **OpenSSL**: Maintain up-to-date openssl gem constraints (see gemspec)
4. **Proxy Support**: Respect proxy settings in secure environments
5. **License Content**: Handle license_content securely (may contain sensitive info)
1. **HTTPS**: Use secure connections to packages.chef.io
1. **OpenSSL**: Maintain up-to-date openssl gem constraints (see gemspec)
1. **Proxy Support**: Respect proxy settings in secure environments
1. **License Content**: Handle license_content securely (may contain sensitive info)

## Release Process

1. Merge PR to main branch
2. Expeditor automatically bumps version (unless skip label)
3. Expeditor builds gem
4. Manual promotion triggers RubyGems publish
5. GitHub release created with version tag (v{{version}})
1. Expeditor automatically bumps version (unless skip label)
1. Expeditor builds gem
1. Manual promotion triggers RubyGems publish
1. GitHub release created with version tag (v{{version}})

## Getting Help

Expand Down Expand Up @@ -333,37 +418,31 @@ When making changes:

---

**Remember**: When in doubt about Ruby version compatibility, check the Gemfile and gemspec for version-specific patterns, and test with Ruby 2.3+ when possible. The goal is maximum compatibility (Ruby 2.3+) without sacrificing functionality.
**Remember**: When in doubt about Ruby version compatibility, check the Gemfile and gemspec for version-specific patterns, and test with Ruby 2.6+ when possible. The goal is maximum compatibility (Ruby 2.6+) without sacrificing functionality.

### Ruby 2.3+ Feature Reference
### Ruby 2.6+ Feature Reference

#### Safe to Use (Ruby 2.3+)
#### Safe to Use (Ruby 2.6+)
- Safe navigation operator: `object&.method`
- Squiggly heredoc: `<<~TEXT`
- `Hash#dig`, `Array#dig`
- `Enumerable#grep_v`
- `Hash#fetch_values`
- `Hash#to_proc`
- Frozen string literal pragma: `# frozen_string_literal: true`

#### Avoid (Ruby 2.4+)
- Endless ranges: `(1..)`
- `Enumerable#chain`
- `Kernel#then`
- `Integer#digits`
- `Comparable#clamp`
- `String#match?`, `Regexp#match?`
- Multiple assignment in conditionals

#### Avoid (Ruby 2.5+)
- `yield_self` / `then`
- `Kernel#yield_self`
- `rescue` in blocks without `begin`

#### Avoid (Ruby 2.6+)
- Endless ranges: `(1..)`
- `Enumerable#chain`
- `Kernel#then`

#### Avoid (Ruby 2.7+)
- Numbered parameters: `_1`, `_2`
- Pattern matching
- `Enumerable#filter_map`
- `Enumerable#tally`
- Method reference operator: `.:`
4 changes: 3 additions & 1 deletion .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,7 @@ jobs:
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
- name: Chef Install Test
- name: Chef Omnibus download Test
run: bundle exec mixlib-install download chef
- name: Chef Licensed download Test
run: bundle exec mixlib-install download chef -L free-79df705d-b685-419a-8b68-88401f74ff72-3999
14 changes: 5 additions & 9 deletions .rubocop.yml
Original file line number Diff line number Diff line change
@@ -1,27 +1,23 @@
---
AllCops:
TargetRubyVersion: 2.3
TargetRubyVersion: 2.6

# The following cops are disabled because their autocorrect features could
# potentially introduce syntax that breaks Ruby 2.3 compatibility, even though
# the cops themselves support Ruby 2.3
# potentially introduce syntax that breaks Ruby 2.6 compatibility, even though
# the cops themselves support Ruby 2.6

# Keep encoding comments for Ruby 2.3 compatibility (they're harmless)
# Keep encoding comments for Ruby 2.6 compatibility (they're harmless)
Style/Encoding:
Enabled: false

# Disable unless we're certain all suggestions work in Ruby 2.3
# Disable unless we're certain all suggestions work in Ruby 2.6
Style/MutableConstant:
Enabled: false

# Security cops that use eval - keep disabled for safety
Security/Eval:
Enabled: false

# Chef-specific cop that suggests require optimizations
Chef/Ruby/UnlessDefinedRequire:
Enabled: false

# Added for Chefstyle 0.12 compatibility, can be removed with switch to cookstyle
Bundler/DuplicatedGem:
Enabled: false
Expand Down
Loading