diff --git a/CODEOWNERS b/.github/CODEOWNERS
similarity index 100%
rename from CODEOWNERS
rename to .github/CODEOWNERS
diff --git a/.github/actions/build-xcframework/action.yml b/.github/actions/build-xcframework/action.yml
new file mode 100644
index 0000000..55afdf8
--- /dev/null
+++ b/.github/actions/build-xcframework/action.yml
@@ -0,0 +1,52 @@
+name: Build XCFramework
+description: Build XCFramework for iOS and iOS Simulator
+
+inputs:
+ project_name:
+ description: Name of the Xcode project or scheme
+ required: true
+
+outputs:
+ xcframework_path:
+ description: Final path to the generated XCFramework
+ value: ${{ steps.build.outputs.xcframework_path }}
+
+runs:
+ using: "composite"
+ steps:
+ - name: Build XCFramework
+ id: build
+ shell: bash
+ run: |
+ set -euo pipefail
+
+ PROJECT_NAME="${{ inputs.project_name }}"
+ BUILD_DIR="./build"
+
+ echo "š ļø Building XCFramework..."
+
+ xcodebuild archive \
+ -scheme "$PROJECT_NAME" \
+ -configuration Release \
+ -destination 'generic/platform=iOS Simulator' \
+ -archivePath "$BUILD_DIR/${PROJECT_NAME}.framework-iphonesimulator.xcarchive" \
+ SKIP_INSTALL=NO \
+ BUILD_LIBRARIES_FOR_DISTRIBUTION=YES | xcbeautify
+
+ xcodebuild archive \
+ -scheme "$PROJECT_NAME" \
+ -configuration Release \
+ -destination 'generic/platform=iOS' \
+ -archivePath "$BUILD_DIR/${PROJECT_NAME}.framework-iphoneos.xcarchive" \
+ SKIP_INSTALL=NO \
+ BUILD_LIBRARIES_FOR_DISTRIBUTION=YES | xcbeautify
+
+ XCFRAMEWORK_PATH="$BUILD_DIR/${PROJECT_NAME}.xcframework"
+
+ xcodebuild -create-xcframework \
+ -framework "$BUILD_DIR/${PROJECT_NAME}.framework-iphonesimulator.xcarchive/Products/Library/Frameworks/${PROJECT_NAME}.framework" \
+ -framework "$BUILD_DIR/${PROJECT_NAME}.framework-iphoneos.xcarchive/Products/Library/Frameworks/${PROJECT_NAME}.framework" \
+ -output "$XCFRAMEWORK_PATH"
+
+ echo "ā
XCFramework built successfully"
+ echo "xcframework_path=$XCFRAMEWORK_PATH" >> "$GITHUB_OUTPUT"
diff --git a/.github/actions/get-project-version/action.yml b/.github/actions/get-project-version/action.yml
new file mode 100644
index 0000000..5ee6a08
--- /dev/null
+++ b/.github/actions/get-project-version/action.yml
@@ -0,0 +1,45 @@
+name: Get Project Version
+description: Extracts MARKETING_VERSION from a .pbxproj file
+
+inputs:
+ project_name:
+ description: Name of the Xcode project (without .xcodeproj)
+ required: false
+ pbxproj_path:
+ description: Path to the project.pbxproj file (overrides project_name if provided)
+ required: false
+
+outputs:
+ version:
+ description: The extracted project version
+ value: ${{ steps.extract.outputs.version }}
+
+runs:
+ using: "composite"
+ steps:
+ - name: Extract current project version
+ id: extract
+ shell: bash
+ run: |
+ set -euo pipefail
+
+ # Determine the pbxproj path
+ if [[ -n "${{ inputs.pbxproj_path }}" ]]; then
+ PBXPROJ="${{ inputs.pbxproj_path }}"
+ elif [[ -n "${{ inputs.project_name }}" ]]; then
+ PBXPROJ="${{ inputs.project_name }}.xcodeproj/project.pbxproj"
+ else
+ echo "ā Either 'project_name' or 'pbxproj_path' must be provided."
+ exit 1
+ fi
+
+ # Check if the pbxproj file exists
+ if [[ ! -f "$PBXPROJ" ]]; then
+ echo "ā Project file not found: $PBXPROJ"
+ exit 1
+ fi
+
+ echo "š¦ Extracting current MARKETING_VERSION from $PBXPROJ..."
+ VERSION=$(grep -m1 'MARKETING_VERSION =' "$PBXPROJ" | sed -E 's/.*MARKETING_VERSION = ([^;]+);/\1/' | xargs)
+ echo "š¢ Current version: $VERSION"
+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
diff --git a/.github/actions/install-dependencies/action.yml b/.github/actions/install-dependencies/action.yml
new file mode 100644
index 0000000..b300ab2
--- /dev/null
+++ b/.github/actions/install-dependencies/action.yml
@@ -0,0 +1,50 @@
+name: Install Dependencies
+description: Checks for Homebrew and installs any missing CLI tools and Ruby gems
+
+inputs:
+ tools:
+ description: Space-separated list of tools to check and install via Homebrew
+ required: false
+ gems:
+ description: Space-separated list of gems to check and install via gem
+ required: false
+
+runs:
+ using: "composite"
+ steps:
+ - name: Install Dependencies
+ shell: bash
+ run: |
+ set -euo pipefail
+
+ if [ -n "${{ inputs.tools }}" ]; then
+ echo "š Checking for Homebrew..."
+ if ! command -v brew >/dev/null; then
+ echo "ā Homebrew is required but not installed. Aborting."
+ exit 1
+ fi
+
+ echo "š§ Installing missing brew tools..."
+ for tool in ${{ inputs.tools }}; do
+ if command -v "$tool" >/dev/null; then
+ echo "ā
$tool is already installed."
+ else
+ echo "š¦ Installing $tool via brew..."
+ brew install "$tool"
+ fi
+ done
+ fi
+
+ if [ -n "${{ inputs.gems }}" ]; then
+ echo "š§ Installing missing gems..."
+ for gem in ${{ inputs.gems }}; do
+ if gem list -i "$gem" >/dev/null; then
+ echo "ā
$gem gem is already installed."
+ else
+ echo "š Installing $gem via gem..."
+ gem install "$gem"
+ fi
+ done
+ fi
+
+ echo "ā
All dependencies are ready."
diff --git a/.github/actions/package-xcframework/action.yaml b/.github/actions/package-xcframework/action.yaml
new file mode 100644
index 0000000..cb6154a
--- /dev/null
+++ b/.github/actions/package-xcframework/action.yaml
@@ -0,0 +1,34 @@
+name: Package XCFramework and LICENSE
+description: Zips the built XCFramework and LICENSE into a versioned release artifact
+
+inputs:
+ package_name:
+ description: The name of the package (e.g., MyLibrary-1.0.0)
+ required: true
+ xcframework_path:
+ description: The path to the built .xcframework
+ required: true
+ license_path:
+ description: The path to the LICENSE file
+ required: true
+
+outputs:
+ zip_name:
+ description: The name of the created zip file
+ value: ${{ steps.package.outputs.zip_name }}
+
+runs:
+ using: "composite"
+ steps:
+ - id: package
+ shell: bash
+ run: |
+ set -euo pipefail
+ ZIP_NAME="${{ inputs.package_name }}.zip"
+ mkdir -p release
+ cp -R "${{ inputs.xcframework_path }}" release/
+ cp "${{ inputs.license_path }}" release/
+ cd release
+ zip -r "../$ZIP_NAME" .
+ echo "ā
Packaged XCFramework and LICENSE into $ZIP_NAME"
+ echo "zip_name=$ZIP_NAME" >> "$GITHUB_OUTPUT"
diff --git a/.github/actions/set-xcode-version/action.yml b/.github/actions/set-xcode-version/action.yml
new file mode 100644
index 0000000..8432bca
--- /dev/null
+++ b/.github/actions/set-xcode-version/action.yml
@@ -0,0 +1,18 @@
+name: Set Xcode Version
+description: Selects the desired Xcode version using xcode-select.
+inputs:
+ xcode-version:
+ description: The Xcode version to select (e.g., 16.4)
+ required: true
+runs:
+ using: 'composite'
+ steps:
+ - run: |
+ set -e
+ echo "Setting Xcode version to ${{ inputs.xcode-version }}..."
+ if ! sudo xcode-select -s /Applications/Xcode_${{ inputs.xcode-version }}.app/Contents/Developer; then
+ echo "ā Failed to select Xcode ${{ inputs.xcode-version }}. Listing available Xcodes:"
+ ls /Applications | grep Xcode
+ exit 1
+ fi
+ shell: bash
diff --git a/pull_request_template.md b/.github/pull_request_template.md
similarity index 100%
rename from pull_request_template.md
rename to .github/pull_request_template.md
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..c58954d
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,180 @@
+name: CI
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ types: [opened, synchronize, reopened]
+
+permissions:
+ contents: read
+ pull-requests: write # For PR comments
+
+env:
+ PROJECT_NAME: OSBarcodeLib
+ SCHEME_NAME: OSBarcodeLib
+ XCODEPROJ_PATH: OSBarcodeLib.xcodeproj
+ XCODE_VERSION: 16.4
+ DESTINATION: 'platform=iOS Simulator,OS=latest,name=iPhone 16'
+ COVERAGE_TARGET_FILTER: OSBarcodeLib
+ BUILD_REPORTS_DIR: build/reports
+ SONAR_REPORTS_DIR: sonar-reports
+
+jobs:
+ test:
+ name: Run Tests
+ runs-on: macos-15
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Install Dependencies
+ uses: ./.github/actions/install-dependencies
+ with:
+ tools: swiftlint xcbeautify
+
+ - name: Set Xcode version
+ uses: ./.github/actions/set-xcode-version
+ with:
+ xcode-version: ${{ env.XCODE_VERSION }}
+
+ - name: Run Unit Tests
+ id: unit_tests
+ env:
+ SCHEME_NAME: ${{ env.SCHEME_NAME }}
+ XCODEPROJ_PATH: ${{ env.XCODEPROJ_PATH }}
+ IOS_SIMULATOR_DEVICE: ${{ env.IOS_SIMULATOR_DEVICE }}
+ DESTINATION: ${{ env.DESTINATION }}
+ run: |
+ set -euo pipefail
+ XCRESULT_NAME="TestResults.xcresult"
+ mkdir -p "$BUILD_REPORTS_DIR"
+ xcodebuild test \
+ -project "$XCODEPROJ_PATH" \
+ -scheme "$SCHEME_NAME" \
+ -destination "$DESTINATION" \
+ -configuration Debug \
+ -enableCodeCoverage YES \
+ -resultBundlePath "$XCRESULT_NAME" \
+ SKIP_SCRIPT_PHASES=YES \
+ CODE_SIGNING_ALLOWED=NO | xcbeautify --report junit --report-path "$BUILD_REPORTS_DIR"
+ echo "xcresult_name=$XCRESULT_NAME" >> "$GITHUB_OUTPUT"
+
+ - name: Generate Code Coverage Report for SonarQube
+ continue-on-error: true
+ env:
+ XCRESULT_NAME: ${{ steps.unit_tests.outputs.xcresult_name }}
+ run: |
+ set -euo pipefail
+ echo "š Generating SonarQube coverage report..."
+
+ if [ ! -d "$XCRESULT_NAME" ]; then
+ echo "ā ļø $XCRESULT_NAME not found. Skipping coverage report generation."
+ exit 0
+ fi
+
+ mkdir -p ${{ env.SONAR_REPORTS_DIR }}
+
+ echo "š¦ Downloading coverage converter script..."
+ curl -sSL https://raw.githubusercontent.com/SonarSource/sonar-scanning-examples/master/swift-coverage/swift-coverage-example/xccov-to-sonarqube-generic.sh -o xccov-to-sonarqube-generic.sh
+ chmod +x xccov-to-sonarqube-generic.sh
+
+ echo "š Running coverage converter..."
+ ./xccov-to-sonarqube-generic.sh TestResults.xcresult > ${{ env.SONAR_REPORTS_DIR }}/sonarqube-generic-coverage.xml
+ echo "ā
SonarQube coverage report generated successfully"
+
+ - name: Run SwiftLint for SonarQube
+ run: |
+ set -euo pipefail
+ echo "š Running SwiftLint..."
+ mkdir -p ${{ env.SONAR_REPORTS_DIR }}
+ swiftlint --reporter checkstyle > "${{ env.SONAR_REPORTS_DIR }}/swiftlint.xml" || {
+ echo "ā ļø SwiftLint finished with issues."
+ exit 0
+ }
+ echo "ā
SwiftLint report generated successfully"
+
+ - name: Setup SonarQube Scanner
+ uses: warchant/setup-sonar-scanner@v8
+
+ - name: Send to SonarCloud
+ id: sonarcloud
+ continue-on-error: true
+ run: |
+ set -euo pipefail
+ if [ -z "${{ secrets.SONAR_TOKEN }}" ]; then
+ echo "ā ļø SONAR_TOKEN secret is not set. Skipping SonarCloud analysis."
+ exit 0
+ fi
+ if [ -f "sonar-project.properties" ]; then
+ echo "š Sending results to SonarCloud..."
+ echo "š¦ Commit: ${{ github.sha }}"
+ if [ "${{ github.ref_name }}" = "main" ]; then
+ echo "š Analyzing main branch"
+ sonar-scanner
+ else
+ echo "šæ Analyzing feature branch: ${{ github.ref_name }}"
+ sonar-scanner -Dsonar.branch.name="${{ github.ref_name }}"
+ fi
+ else
+ echo "ā ļø sonar-project.properties not found, skipping SonarCloud"
+ fi
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+
+ - name: Upload Test Results
+ uses: actions/upload-artifact@v4
+ if: always()
+ with:
+ name: test-results
+ path: |
+ ${{ steps.unit_tests.outputs.xcresult_name }}
+ ${{ env.SONAR_REPORTS_DIR }}
+ ${{ env.BUILD_REPORTS_DIR }}
+
+ - name: Comment Test Results
+ if: github.event_name == 'pull_request'
+ uses: actions/github-script@v7
+ env:
+ XCRESULT_NAME: ${{ steps.unit_tests.outputs.xcresult_name }}
+ COVERAGE_TARGET_FILTER: ${{ env.COVERAGE_TARGET_FILTER }}
+ with:
+ script: |
+ const { execSync } = require('child_process');
+ const fs = require('fs');
+
+ console.log('š Starting to comment test results...');
+ let coveragePercentage = 'N/A';
+ try {
+ const xcresultName = process.env.XCRESULT_NAME;
+ const coverageTarget = process.env.COVERAGE_TARGET_FILTER;
+ console.log(`Checking result file: ${xcresultName}`);
+ if (fs.existsSync(xcresultName)) {
+ console.log('Result file found. Calculating coverage...');
+ const output = execSync(`xcrun xccov view --report "${xcresultName}"`).toString();
+ const match = output.match(new RegExp(`${coverageTarget}.*?([0-9]+\\.[0-9]+%)`));
+ if (match && match[1]) {
+ coveragePercentage = match[1];
+ console.log(`Coverage found: ${coveragePercentage}`);
+ } else {
+ console.log('Coverage not found in report.');
+ }
+ } else {
+ console.log('Result file not found.');
+ }
+ } catch (e) {
+ console.error('Error calculating coverage:', e);
+ coveragePercentage = 'N/A';
+ }
+
+ console.log('Commenting on PR with test results and coverage...');
+ await github.rest.issues.createComment({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ body: `ā
**Tests**: All passed\nš **Coverage**: ${coveragePercentage}`
+ });
+ console.log('Comment sent successfully.');
diff --git a/.github/workflows/github_actions.yml b/.github/workflows/github_actions.yml
deleted file mode 100644
index 95c71a2..0000000
--- a/.github/workflows/github_actions.yml
+++ /dev/null
@@ -1,46 +0,0 @@
-name: GitHub Actions
-
-on:
- pull_request:
- types: [opened, synchronize, reopened]
-
-jobs:
- test:
- name: Unit-Tests
- runs-on: macos-14
- steps:
- - name: Checkout
- uses: actions/checkout@v4
-
- - name: Setup Java 17
- uses: actions/setup-java@v4
- with:
- distribution: 'zulu'
- java-version: '17'
-
- - name: Link SwiftLint or install it
- run: brew link --overwrite swiftlint || brew install swiftlint
-
- - name: Set up XCode
- run: sudo xcode-select --switch /Applications/Xcode_15.0.app
-
- - name: Bundle Install
- run: bundle install
-
- - name: Unit tests
- run: bundle exec fastlane unit_tests
-
- - name: Code Coverage
- run: bundle exec fastlane coverage
-
- - name: Lint
- run: bundle exec fastlane lint
-
- - name: Setup sonarqube
- uses: warchant/setup-sonar-scanner@v8
-
- - name: Send to Sonarcloud
- run: bundle exec fastlane sonarqube
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- SONAR_TOKEN: ${{ secrets.SONARCLOUD_KEY }}
diff --git a/.github/workflows/prepare_release.yml b/.github/workflows/prepare_release.yml
index 70212ec..fa04da9 100644
--- a/.github/workflows/prepare_release.yml
+++ b/.github/workflows/prepare_release.yml
@@ -3,79 +3,147 @@ name: Prepare Release
on:
workflow_dispatch:
inputs:
- versionBumpLevel:
- description: 'Version bump level (patch, minor, major)'
- required: true
+ bump:
+ description: 'Version bump type (ignored if version is set)'
+ required: false
type: choice
- default: 'patch'
options:
- - patch
- - minor
- - major
+ - patch
+ - minor
+ - major
+ version:
+ description: 'Set specific version (e.g., 1.2.3)'
+ required: false
+
+env:
+ PROJECT_NAME: OSBarcodeLib
+ PBXPROJ: OSBarcodeLib.xcodeproj/project.pbxproj
+ XCODE_VERSION: 16.4
+ CHANGELOG_PATH: CHANGELOG.md
+ LICENSE_PATH: LICENSE
+ PODSPEC_FILE: OSBarcodeLib.podspec
jobs:
- build-and-release:
- if: github.ref == 'refs/heads/main'
- runs-on: macos-14
+ prepare-release:
+ name: Prepare Release
+ runs-on: macos-15
+ if: github.ref_name == 'main'
+
steps:
- - name: Checkout
+ - name: Checkout code
uses: actions/checkout@v4
-
- - name: Link SwiftLint or install it
- run: brew link --overwrite swiftlint || brew install swiftlint
-
- - name: Set up XCode
- run: sudo xcode-select --switch /Applications/Xcode_15.0.app
-
- - name: Set up Ruby
- uses: ruby/setup-ruby@v1
with:
- ruby-version: '3.3'
-
- - name: Bump version
- run: ruby ./scripts/bump_versions.rb ${{ github.event.inputs.versionBumpLevel }}
+ fetch-depth: 0
- - name: Build XCFramework
- run: ./scripts/build_framework.sh
+ - name: Install Dependencies
+ uses: ./.github/actions/install-dependencies
+ with:
+ tools: gh
- - name: Get new version
- id: version
- run: echo "VERSION=$(ruby -e 'puts File.read("./OSBarcodeLib.podspec").match(/spec.version.*=.*''(\d+\.\d+\.\d+)''/)[1]')" >> $GITHUB_ENV
+ - name: Get current project version
+ id: get_version
+ uses: ./.github/actions/get-project-version
+ with:
+ project_name: ${{ env.PROJECT_NAME }}
- - name: Create new branch
+ - name: Bump project version
+ id: bump_version
+ env:
+ CURRENT_VERSION: ${{ steps.get_version.outputs.version }}
+ INPUT_BUMP: ${{ github.event.inputs.bump }}
+ INPUT_VERSION: ${{ github.event.inputs.version }}
run: |
- git switch --create "prepare-new-release-${{ env.VERSION }}"
+ set -euo pipefail
+ echo "ā¬ļø Bumping project version..."
+ if [ -n "$INPUT_VERSION" ]; then
+ if ! [[ "$INPUT_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
+ echo "ā Version must be in format M.m.p (e.g., 1.2.3)"
+ exit 1
+ fi
+ NEW_VERSION="$INPUT_VERSION"
+ else
+ IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION"
+ PATCH="${PATCH:-0}"
+ case "$INPUT_BUMP" in
+ major)
+ MAJOR=$((MAJOR+1)); MINOR=0; PATCH=0;;
+ minor)
+ MINOR=$((MINOR+1)); PATCH=0;;
+ *)
+ PATCH=$((PATCH+1));;
+ esac
+ NEW_VERSION="$MAJOR.$MINOR.$PATCH"
+ fi
+ echo "š¢ New version: $NEW_VERSION"
+ echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
- - name: Move zip file to root and push changes
+ - name: Update project files
+ env:
+ PBXPROJ: ${{ env.PBXPROJ }}
+ PODSPEC_FILE: ${{ env.PODSPEC_FILE }}
+ CHANGELOG_PATH: ${{ env.CHANGELOG_PATH }}
+ NEW_VERSION: ${{ steps.bump_version.outputs.new_version }}
run: |
- if [ -f OSBarcodeLib.zip ]; then
- rm OSBarcodeLib.zip
+ set -euo pipefail
+
+ echo "š ļø Updating MARKETING_VERSION..."
+ sed -i '' -E "s/MARKETING_VERSION = [0-9]+\.[0-9]+(\.[0-9]+)?;/MARKETING_VERSION = $NEW_VERSION;/g" "$PBXPROJ"
+ echo "š ļø Updating CURRENT_PROJECT_VERSION..."
+ current_proj_version=$(grep -m1 'CURRENT_PROJECT_VERSION =' "$PBXPROJ" | sed -E 's/.*CURRENT_PROJECT_VERSION = ([0-9]+);/\1/')
+ new_proj_version=$((current_proj_version+1))
+ sed -i '' -E "s/CURRENT_PROJECT_VERSION = [0-9]+;/CURRENT_PROJECT_VERSION = $new_proj_version;/g" "$PBXPROJ"
+ echo "ā
Bumped MARKETING_VERSION to $NEW_VERSION, CURRENT_PROJECT_VERSION to $new_proj_version"
+
+ echo "š ļø Updating version in $PODSPEC_FILE..."
+ if [ -f "$PODSPEC_FILE" ]; then
+ sed -i '' -E "s/^([[:space:]]*[^[:space:]]+\.version[[:space:]]*=[[:space:]]*['\"])[^'\"]+(['\"])/\1$NEW_VERSION\2/" "$PODSPEC_FILE"
+ echo "ā
Podspec version updated to $NEW_VERSION"
else
- echo "File does not exist."
+ echo "ā ļø Podspec file not found: $PODSPEC_FILE"
fi
- mv build/OSBarcodeLib.zip .
- git config --global user.name 'github-actions[bot]'
- git config --global user.email 'github-actions[bot]@users.noreply.github.com'
- git add .
- git commit -m "chore: Bump version to ${{ env.VERSION }}"
- git push origin HEAD:prepare-new-release-${{ env.VERSION }}
- - name: Create pull request
- id: create_pr
+ echo "š Updating $CHANGELOG_PATH..."
+ TODAY=$(date +%Y-%m-%d)
+ awk -v ver="$NEW_VERSION" -v today="$TODAY" '
+ BEGIN { unreleased_found=0 }
+ /^## \[Unreleased\]/ {
+ print $0; print ""; print "## [" ver "] - " today; unreleased_found=1; next
+ }
+ { print $0 }
+ ' "$CHANGELOG_PATH" > "$CHANGELOG_PATH.tmp" && mv "$CHANGELOG_PATH.tmp" "$CHANGELOG_PATH"
+ echo "ā
CHANGELOG updated for version $NEW_VERSION"
+
+ - name: Create release branch, commit and push changes
+ id: create_release_branch
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ VERSION: ${{ steps.bump_version.outputs.new_version }}
run: |
- gh pr create -B main -H prepare-new-release-${{ env.VERSION }} --title 'Prepare `main` to Release `${{ env.VERSION }}`' --body 'Bumps version to `${{ env.VERSION }}`.
Creates an updated and ready-to-be-released `OSBarcodeLib.zip`.'
- PR_NUMBER=$(gh pr view --json number --jq '.number')
- echo "PR_NUMBER=${PR_NUMBER}" >> $GITHUB_ENV
+ set -euo pipefail
+ BRANCH_NAME="release/v$VERSION"
+ git config user.name "github-actions[bot] (on behalf of ${{ github.actor }})"
+ git config user.email "github-actions[bot]@users.noreply.github.com"
+ git checkout -b "$BRANCH_NAME"
+ git add .
+ git commit -m "chore(release): prepare release v$VERSION"
+ git push origin "$BRANCH_NAME"
+ echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
+
+ - name: Ensure 'release' label exists
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: gh label create release --color FFD700 --description "Release PRs" || true
- - name: Add label to the pull request
- run: |
- gh api \
- --method POST \
- -H "Accept: application/vnd.github+json" \
- -H "X-GitHub-Api-Version: 2022-11-28" \
- /repos/${{ github.repository }}/issues/${{ env.PR_NUMBER }}/labels \
- -f "labels[]=release"
+ - name: Create Pull Request
env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ VERSION: ${{ steps.bump_version.outputs.new_version }}
+ BRANCH_NAME: ${{ steps.create_release_branch.outputs.branch_name }}
+ run: |
+ set -euo pipefail
+ gh pr create \
+ --title "Release v$VERSION" \
+ --body "Automated PR to release v$VERSION." \
+ --head $BRANCH_NAME \
+ --base main \
+ --label release
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..84641d1
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,145 @@
+name: Release
+
+on:
+ pull_request:
+ types: [closed]
+ branches:
+ - 'main'
+
+env:
+ PROJECT_NAME: OSBarcodeLib
+ XCODE_VERSION: 16.4
+ CHANGELOG_PATH: CHANGELOG.md
+ LICENSE_PATH: LICENSE
+ PODSPEC_FILE: OSBarcodeLib.podspec
+
+jobs:
+ tag_and_release:
+ name: Release and Publish
+ runs-on: macos-15
+ if: >-
+ github.event.pull_request.merged == true &&
+ contains(github.event.pull_request.labels.*.name, 'release')
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Install Dependencies
+ uses: ./.github/actions/install-dependencies
+ with:
+ tools: gh
+ gems: cocoapods
+
+ - name: Get current project version
+ id: get_version
+ uses: ./.github/actions/get-project-version
+ with:
+ project_name: ${{ env.PROJECT_NAME }}
+
+ - name: Get release notes for this version
+ id: release_notes
+ env:
+ VERSION: ${{ steps.get_version.outputs.version }}
+ CHANGELOG_PATH: ${{ env.CHANGELOG_PATH }}
+ run: |
+ set -euo pipefail
+ if [ ! -f "$CHANGELOG_PATH" ]; then
+ echo "ā Changelog file not found: $CHANGELOG_PATH"
+ exit 1
+ fi
+ echo "š Extracting release notes for version $VERSION..."
+ release_notes_section=$(awk "/^## \[${VERSION}\]/ {flag=1; next} flag && /^## \\[/ {exit} flag {print}" "$CHANGELOG_PATH" | sed '/^\s*$/d')
+ if [ -n "$release_notes_section" ]; then
+ echo "$release_notes_section"
+ echo 'release_notes<> $GITHUB_OUTPUT
+ echo "$release_notes_section" >> $GITHUB_OUTPUT
+ echo 'EOF' >> $GITHUB_OUTPUT
+ else
+ echo "ā ļø No release notes found for version $VERSION."
+ exit 0
+ fi
+
+ - name: Set Xcode version
+ uses: ./.github/actions/set-xcode-version
+ with:
+ xcode-version: ${{ env.XCODE_VERSION }}
+
+ - name: Build XCFramework
+ id: build_xcframework
+ uses: ./.github/actions/build-xcframework
+ with:
+ project_name: ${{ env.PROJECT_NAME }}
+
+ - name: Package XCFramework
+ uses: ./.github/actions/package-xcframework
+ id: package
+ with:
+ package_name: ${{ env.PROJECT_NAME }}
+ xcframework_path: ${{ steps.build_xcframework.outputs.xcframework_path }}
+ license_path: ${{ env.LICENSE_PATH }}
+
+ - name: Create tag
+ id: create_tag
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ VERSION: ${{ steps.get_version.outputs.version }}
+ run: |
+ set -euo pipefail
+ git config user.name "github-actions[bot]"
+ git config user.email "github-actions[bot]@users.noreply.github.com"
+ git tag v$VERSION -m "Release version $VERSION"
+ git push origin v$VERSION
+ echo "Tag v$VERSION created."
+ echo "tag=v$VERSION" >> $GITHUB_OUTPUT
+
+ - name: Create GitHub Release
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ TITLE: ${{ steps.create_tag.outputs.tag }}
+ RELEASE_NOTES: ${{ steps.release_notes.outputs.release_notes }}
+ ASSET_PATH: ${{ steps.package.outputs.zip_name }}
+ run: |
+ set -euo pipefail
+ gh release create $TITLE \
+ --title "$TITLE" \
+ --notes "$RELEASE_NOTES" \
+ "$ASSET_PATH"
+
+ - name: Publish Pod
+ if: hashFiles('${{ env.PODSPEC_FILE }}') != ''
+ env:
+ COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }}
+ run: |
+ set -euo pipefail
+ if [ -z "${COCOAPODS_TRUNK_TOKEN:-}" ]; then
+ echo "ā COCOAPODS_TRUNK_TOKEN secret is not set. Please set it in your repository secrets."
+ exit 1
+ fi
+ echo "š Deploying podspec to CocoaPods..."
+ pod trunk push "$PODSPEC_FILE" --allow-warnings
+
+ - name: Delete source branch
+ if: github.event.pull_request.head.ref != 'main'
+ run: |
+ set +e
+ git push origin --delete ${{ github.event.pull_request.head.ref }}
+ set -e
+
+ delete_branch_if_pr_closed_without_merge:
+ name: Delete Source Branch If PR Closed Without Merge
+ runs-on: macos-15
+ if: >-
+ github.event.pull_request.merged == false &&
+ github.event.pull_request.head.ref != 'main' &&
+ contains(github.event.pull_request.labels.*.name, 'release')
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ - name: Delete source branch
+ run: |
+ set +e
+ git push origin --delete ${{ github.event.pull_request.head.ref }}
+ set -e
diff --git a/.github/workflows/release_and_publish.yml b/.github/workflows/release_and_publish.yml
deleted file mode 100644
index 92cc14a..0000000
--- a/.github/workflows/release_and_publish.yml
+++ /dev/null
@@ -1,67 +0,0 @@
-name: Release and Publish
-
-on:
- pull_request:
- types: [closed]
- branches:
- - 'main'
-
-jobs:
- post-merge:
- if: contains(github.event.pull_request.labels.*.name, 'release') && github.event.pull_request.merged == true
- runs-on: macos-14
-
- steps:
- - name: Checkout Repository
- uses: actions/checkout@v4
-
- - name: Set up Cocoapods
- run: gem install cocoapods
-
- - name: Get new version
- id: version
- run: echo "VERSION=$(ruby -e 'puts File.read("./OSBarcodeLib.podspec").match(/spec.version.*=.*''(\d+\.\d+\.\d+)''/)[1]')" >> $GITHUB_ENV
-
- - name: Extract release notes
- run: sh scripts/extract_release_notes.sh "${{ env.VERSION }}" >> release_notes.md
-
- - name: Create Tag
- id: create_tag
- run: |
- # Define the tag name and message
- TAG_NAME="${{ env.VERSION }}"
- TAG_MESSAGE="Tag for version ${{ env.VERSION }}"
-
- # Create the tag
- git tag -a "$TAG_NAME" -m "$TAG_MESSAGE"
- git push origin "$TAG_NAME"
-
- echo "Tag created: $TAG_NAME"
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Create Release
- run: |
- # Extract the tag name
- TAG_NAME="${{ env.VERSION }}"
- RELEASE_NOTES="$(cat release_notes.md)"
-
- # Create the release using GitHub CLI
- gh release create "$TAG_NAME" \
- --title "$TAG_NAME" \
- --notes "$RELEASE_NOTES" \
- "OSBarcodeLib.zip"
-
- echo "Release created for tag: $TAG_NAME"
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Deploy to Cocoapods
- run: pod trunk push ./OSBarcodeLib.podspec --allow-warnings
- env:
- COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }}
-
- - name: Delete Release Branch
- run: git push origin --delete prepare-new-release-${{ env.VERSION }}
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/Gemfile b/Gemfile
deleted file mode 100644
index a80b326..0000000
--- a/Gemfile
+++ /dev/null
@@ -1,4 +0,0 @@
-source "https://rubygems.org"
-
-gem "fastlane"
-gem "slather"
\ No newline at end of file
diff --git a/Gemfile.lock b/Gemfile.lock
deleted file mode 100644
index f8ead4f..0000000
--- a/Gemfile.lock
+++ /dev/null
@@ -1,267 +0,0 @@
-GEM
- remote: https://rubygems.org/
- specs:
- CFPropertyList (3.0.7)
- base64
- nkf
- rexml
- activesupport (7.2.0)
- base64
- bigdecimal
- concurrent-ruby (~> 1.0, >= 1.3.1)
- connection_pool (>= 2.2.5)
- drb
- i18n (>= 1.6, < 2)
- logger (>= 1.4.2)
- minitest (>= 5.1)
- securerandom (>= 0.3)
- tzinfo (~> 2.0, >= 2.0.5)
- addressable (2.8.7)
- public_suffix (>= 2.0.2, < 7.0)
- artifactory (3.0.17)
- atomos (0.1.3)
- aws-eventstream (1.3.0)
- aws-partitions (1.963.0)
- aws-sdk-core (3.201.4)
- aws-eventstream (~> 1, >= 1.3.0)
- aws-partitions (~> 1, >= 1.651.0)
- aws-sigv4 (~> 1.8)
- jmespath (~> 1, >= 1.6.1)
- aws-sdk-kms (1.88.0)
- aws-sdk-core (~> 3, >= 3.201.0)
- aws-sigv4 (~> 1.5)
- aws-sdk-s3 (1.157.0)
- aws-sdk-core (~> 3, >= 3.201.0)
- aws-sdk-kms (~> 1)
- aws-sigv4 (~> 1.5)
- aws-sigv4 (1.9.1)
- aws-eventstream (~> 1, >= 1.0.2)
- babosa (1.0.4)
- base64 (0.2.0)
- bigdecimal (3.1.8)
- claide (1.1.0)
- clamp (1.3.2)
- colored (1.2)
- colored2 (3.1.2)
- commander (4.6.0)
- highline (~> 2.0.0)
- concurrent-ruby (1.3.4)
- connection_pool (2.4.1)
- declarative (0.0.20)
- digest-crc (0.6.5)
- rake (>= 12.0.0, < 14.0.0)
- domain_name (0.6.20240107)
- dotenv (2.8.1)
- drb (2.2.1)
- emoji_regex (3.2.3)
- excon (0.111.0)
- faraday (1.10.3)
- faraday-em_http (~> 1.0)
- faraday-em_synchrony (~> 1.0)
- faraday-excon (~> 1.1)
- faraday-httpclient (~> 1.0)
- faraday-multipart (~> 1.0)
- faraday-net_http (~> 1.0)
- faraday-net_http_persistent (~> 1.0)
- faraday-patron (~> 1.0)
- faraday-rack (~> 1.0)
- faraday-retry (~> 1.0)
- ruby2_keywords (>= 0.0.4)
- faraday-cookie_jar (0.0.7)
- faraday (>= 0.8.0)
- http-cookie (~> 1.0.0)
- faraday-em_http (1.0.0)
- faraday-em_synchrony (1.0.0)
- faraday-excon (1.1.0)
- faraday-httpclient (1.0.1)
- faraday-multipart (1.0.4)
- multipart-post (~> 2)
- faraday-net_http (1.0.2)
- faraday-net_http_persistent (1.2.0)
- faraday-patron (1.0.0)
- faraday-rack (1.0.0)
- faraday-retry (1.0.3)
- faraday_middleware (1.2.0)
- faraday (~> 1.0)
- fastimage (2.3.1)
- fastlane (2.222.0)
- CFPropertyList (>= 2.3, < 4.0.0)
- addressable (>= 2.8, < 3.0.0)
- artifactory (~> 3.0)
- aws-sdk-s3 (~> 1.0)
- babosa (>= 1.0.3, < 2.0.0)
- bundler (>= 1.12.0, < 3.0.0)
- colored (~> 1.2)
- commander (~> 4.6)
- dotenv (>= 2.1.1, < 3.0.0)
- emoji_regex (>= 0.1, < 4.0)
- excon (>= 0.71.0, < 1.0.0)
- faraday (~> 1.0)
- faraday-cookie_jar (~> 0.0.6)
- faraday_middleware (~> 1.0)
- fastimage (>= 2.1.0, < 3.0.0)
- gh_inspector (>= 1.1.2, < 2.0.0)
- google-apis-androidpublisher_v3 (~> 0.3)
- google-apis-playcustomapp_v1 (~> 0.1)
- google-cloud-env (>= 1.6.0, < 2.0.0)
- google-cloud-storage (~> 1.31)
- highline (~> 2.0)
- http-cookie (~> 1.0.5)
- json (< 3.0.0)
- jwt (>= 2.1.0, < 3)
- mini_magick (>= 4.9.4, < 5.0.0)
- multipart-post (>= 2.0.0, < 3.0.0)
- naturally (~> 2.2)
- optparse (>= 0.1.1, < 1.0.0)
- plist (>= 3.1.0, < 4.0.0)
- rubyzip (>= 2.0.0, < 3.0.0)
- security (= 0.1.5)
- simctl (~> 1.6.3)
- terminal-notifier (>= 2.0.0, < 3.0.0)
- terminal-table (~> 3)
- tty-screen (>= 0.6.3, < 1.0.0)
- tty-spinner (>= 0.8.0, < 1.0.0)
- word_wrap (~> 1.0.0)
- xcodeproj (>= 1.13.0, < 2.0.0)
- xcpretty (~> 0.3.0)
- xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
- gh_inspector (1.1.3)
- google-apis-androidpublisher_v3 (0.54.0)
- google-apis-core (>= 0.11.0, < 2.a)
- google-apis-core (0.11.3)
- addressable (~> 2.5, >= 2.5.1)
- googleauth (>= 0.16.2, < 2.a)
- httpclient (>= 2.8.1, < 3.a)
- mini_mime (~> 1.0)
- representable (~> 3.0)
- retriable (>= 2.0, < 4.a)
- rexml
- google-apis-iamcredentials_v1 (0.17.0)
- google-apis-core (>= 0.11.0, < 2.a)
- google-apis-playcustomapp_v1 (0.13.0)
- google-apis-core (>= 0.11.0, < 2.a)
- google-apis-storage_v1 (0.31.0)
- google-apis-core (>= 0.11.0, < 2.a)
- google-cloud-core (1.7.1)
- google-cloud-env (>= 1.0, < 3.a)
- google-cloud-errors (~> 1.0)
- google-cloud-env (1.6.0)
- faraday (>= 0.17.3, < 3.0)
- google-cloud-errors (1.4.0)
- google-cloud-storage (1.47.0)
- addressable (~> 2.8)
- digest-crc (~> 0.4)
- google-apis-iamcredentials_v1 (~> 0.1)
- google-apis-storage_v1 (~> 0.31.0)
- google-cloud-core (~> 1.6)
- googleauth (>= 0.16.2, < 2.a)
- mini_mime (~> 1.0)
- googleauth (1.8.1)
- faraday (>= 0.17.3, < 3.a)
- jwt (>= 1.4, < 3.0)
- multi_json (~> 1.11)
- os (>= 0.9, < 2.0)
- signet (>= 0.16, < 2.a)
- highline (2.0.3)
- http-cookie (1.0.6)
- domain_name (~> 0.5)
- httpclient (2.8.3)
- i18n (1.14.5)
- concurrent-ruby (~> 1.0)
- jmespath (1.6.2)
- json (2.7.2)
- jwt (2.8.2)
- base64
- logger (1.6.0)
- mini_magick (4.13.2)
- mini_mime (1.1.5)
- minitest (5.24.1)
- multi_json (1.15.0)
- multipart-post (2.4.1)
- nanaimo (0.3.0)
- naturally (2.2.1)
- nkf (0.2.0)
- nokogiri (1.16.7-aarch64-linux)
- racc (~> 1.4)
- nokogiri (1.16.7-arm-linux)
- racc (~> 1.4)
- nokogiri (1.16.7-arm64-darwin)
- racc (~> 1.4)
- nokogiri (1.16.7-x86-linux)
- racc (~> 1.4)
- nokogiri (1.16.7-x86_64-darwin)
- racc (~> 1.4)
- nokogiri (1.16.7-x86_64-linux)
- racc (~> 1.4)
- optparse (0.5.0)
- os (1.1.4)
- plist (3.7.1)
- public_suffix (6.0.1)
- racc (1.8.1)
- rake (13.2.1)
- representable (3.2.0)
- declarative (< 0.1.0)
- trailblazer-option (>= 0.1.1, < 0.2.0)
- uber (< 0.2.0)
- retriable (3.1.2)
- rexml (3.3.5)
- strscan
- rouge (2.0.7)
- ruby2_keywords (0.0.5)
- rubyzip (2.3.2)
- securerandom (0.3.1)
- security (0.1.5)
- signet (0.19.0)
- addressable (~> 2.8)
- faraday (>= 0.17.5, < 3.a)
- jwt (>= 1.5, < 3.0)
- multi_json (~> 1.10)
- simctl (1.6.10)
- CFPropertyList
- naturally
- slather (2.8.3)
- CFPropertyList (>= 2.2, < 4)
- activesupport
- clamp (~> 1.3)
- nokogiri (>= 1.14.3)
- xcodeproj (~> 1.21)
- strscan (3.1.0)
- terminal-notifier (2.0.0)
- terminal-table (3.0.2)
- unicode-display_width (>= 1.1.1, < 3)
- trailblazer-option (0.1.2)
- tty-cursor (0.7.1)
- tty-screen (0.8.2)
- tty-spinner (0.9.3)
- tty-cursor (~> 0.7)
- tzinfo (2.0.6)
- concurrent-ruby (~> 1.0)
- uber (0.1.0)
- unicode-display_width (2.5.0)
- word_wrap (1.0.0)
- xcodeproj (1.25.0)
- CFPropertyList (>= 2.3.3, < 4.0)
- atomos (~> 0.1.3)
- claide (>= 1.0.2, < 2.0)
- colored2 (~> 3.1)
- nanaimo (~> 0.3.0)
- rexml (>= 3.3.2, < 4.0)
- xcpretty (0.3.0)
- rouge (~> 2.0.7)
- xcpretty-travis-formatter (1.0.1)
- xcpretty (~> 0.2, >= 0.0.7)
-
-PLATFORMS
- aarch64-linux
- arm-linux
- arm64-darwin
- x86-linux
- x86_64-darwin
- x86_64-linux
-
-DEPENDENCIES
- fastlane
- slather
-
-BUNDLED WITH
- 2.5.10
diff --git a/OSBarcodeLib.zip b/OSBarcodeLib.zip
deleted file mode 100644
index 6e4b5c7..0000000
Binary files a/OSBarcodeLib.zip and /dev/null differ
diff --git a/fastlane/Appfile b/fastlane/Appfile
deleted file mode 100644
index 1803063..0000000
--- a/fastlane/Appfile
+++ /dev/null
@@ -1,6 +0,0 @@
-# app_identifier("[[APP_IDENTIFIER]]") # The bundle identifier of your app
-# apple_id("[[APPLE_ID]]") # Your Apple email address
-
-
-# For more information about the Appfile, see:
-# https://docs.fastlane.tools/advanced/#appfile
diff --git a/fastlane/Fastfile b/fastlane/Fastfile
deleted file mode 100644
index 686d228..0000000
--- a/fastlane/Fastfile
+++ /dev/null
@@ -1,44 +0,0 @@
-# This file contains the fastlane.tools configuration
-# You can find the documentation at https://docs.fastlane.tools
-#
-# For a list of all available actions, check out
-#
-# https://docs.fastlane.tools/actions
-#
-# For a list of all available plugins, check out
-#
-# https://docs.fastlane.tools/plugins/available-plugins
-#
-
-# Uncomment the line if you want fastlane to automatically update itself
-# update_fastlane
-
-default_platform(:ios)
-
-platform :ios do
- desc "Lane to run the unit tests"
- lane :unit_tests do
- run_tests(scheme: "OSBarcodeLib")
- end
-
- desc "Code coverage"
- lane :coverage do
- slather(
- scheme: "OSBarcodeLib",
- proj: "OSBarcodeLib.xcodeproj",
- output_directory: "sonar-reports",
- sonarqube_xml: "true"
- )
- end
-
- lane :lint do
- swiftlint(
- output_file: "sonar-reports/OSBarcodeLib-swiftlint.txt",
- ignore_exit_status: true
- )
- end
-
- lane :sonarqube do
- sonar
- end
-end
diff --git a/fastlane/README.md b/fastlane/README.md
deleted file mode 100644
index bd01748..0000000
--- a/fastlane/README.md
+++ /dev/null
@@ -1,32 +0,0 @@
-fastlane documentation
-----
-
-# Installation
-
-Make sure you have the latest version of the Xcode command line tools installed:
-
-```sh
-xcode-select --install
-```
-
-For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane)
-
-# Available Actions
-
-## iOS
-
-### ios unit_tests
-
-```sh
-[bundle exec] fastlane ios unit_tests
-```
-
-Lane to run the unit tests
-
-----
-
-This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.
-
-More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools).
-
-The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools).