diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 73a2f39..67c59c2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,7 +1,7 @@ -# This workflow will build a Swift project +# This workflow will build and test the Swift project # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-swift -name: Test and Deploy Framework +name: Test Framework on: push: @@ -10,27 +10,19 @@ on: - develop paths: - .github/workflows/** - - .swiftlint.yml + - .swift-format - Package.swift - Punycode.podspec - Punycode.xcodeproj/** - Sources/** - Tests/** - # pull_request: - # branches: - # - develop - # # branches-ignore: - # # - "release/**" - # # - "feature/**" - # paths: - # - .github/workflows/** - # - .swiftlint.yml - # - Package.swift - # - Punycode.podspec - # - Punycode.xcodeproj/** - # - Sources/** - # - Tests/** - + ### No paths filter on pull_request: the "CI Success" job is a required status + ### check on main, so it must report on every PR (a filtered-out workflow would + ### leave the check "expected" forever and block merging docs-only PRs). + pull_request: + branches: + - main + - develop env: project_name: Punycode @@ -57,46 +49,26 @@ jobs: all_files: false # default is false max_warnings: -1 # default is -1 (infinity) - ### ref: https://github.com/Alamofire/Alamofire/blob/master/.github/workflows/ci.yml + ### Uses the default Xcode of each runner image so the matrix keeps working as images update. test_macOS: name: ${{ matrix.name }} runs-on: ${{ matrix.runsOn }} - env: - DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" timeout-minutes: 10 strategy: fail-fast: false matrix: include: - - xcode: "Xcode_15.4" - runsOn: macos-14 - name: "Test: macOS 14, Xcode 15.4, Swift 5.10" - testPlan: "macOS" - outputFilter: xcbeautify --renderer github-actions + - runsOn: macos-26 + name: "Test: macOS 26, Xcode 26" coverage: YES - - xcode: "Xcode_15.2" - runsOn: macos-14 - name: "Test: macOS 14, Xcode 15.2, Swift 5.9.2" - testPlan: "macOS" - outputFilter: xcbeautify --renderer github-actions - coverage: NO - - xcode: "Xcode_14.3.1" - runsOn: macOS-13 - name: "Test: macOS 13, Xcode 14.3.1, Swift 5.8.0" - testPlan: "macOS" - outputFilter: xcbeautify --renderer github-actions - coverage: NO - - xcode: "Xcode_14.2" - runsOn: macOS-12 - name: "Test: macOS 12, Xcode 14.2, Swift 5.7.2" - testPlan: "macOS" - outputFilter: xcpretty + - runsOn: macos-15 + name: "Test: macOS 15, Xcode 16.4" coverage: NO steps: - uses: actions/checkout@v4 - name: ${{ matrix.name }} run: |- - set -o pipefail && env NSUnbufferedIO=YES xcodebuild -project "${{ env.project_name }}.xcodeproj" -scheme ${{ env.scheme }} -destination "platform=macOS" -enableCodeCoverage ${{ matrix.coverage }} clean test | ${{ matrix.outputFilter }} + set -o pipefail && env NSUnbufferedIO=YES xcodebuild -project "${{ env.project_name }}.xcodeproj" -scheme ${{ env.scheme }} -destination "platform=macOS" -enableCodeCoverage ${{ matrix.coverage }} clean test | xcbeautify --renderer github-actions - name: Upload coverage to Codecov if: ${{ matrix.coverage == 'YES' }} uses: codecov/codecov-action@v4 @@ -107,285 +79,108 @@ jobs: needs: lint_code test_Catalyst: - name: ${{ matrix.name }} - runs-on: ${{ matrix.runsOn }} - env: - DEVELOPER_DIR: /Applications/${{ matrix.xcode }}.app/Contents/Developer + name: "Test: Catalyst" + runs-on: macos-26 timeout-minutes: 10 - strategy: - fail-fast: false - matrix: - include: - - xcode: "Xcode_15.4" - name: "Test: Catalyst 15.4" - runsOn: macos-14 - - xcode: "Xcode_14.3.1" - name: "Test: Catalyst 14.3.1" - runsOn: macOS-13 steps: - uses: actions/checkout@v4 - name: Catalyst - run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -project "${{ env.project_name }}.xcodeproj" -scheme ${{ env.scheme }} -destination "platform=macOS" clean test 2>&1 | xcbeautify --renderer github-actions + run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -project "${{ env.project_name }}.xcodeproj" -scheme ${{ env.scheme }} -destination "platform=macOS,variant=Mac Catalyst" CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO clean test 2>&1 | xcbeautify --renderer github-actions needs: lint_code - test_iOS: + ### Simulator device names change with every Xcode release, so resolve an available + ### device at runtime via simctl instead of hardcoding names that rot. + test_simulators: name: ${{ matrix.name }} runs-on: ${{ matrix.runsOn }} - env: - DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" - timeout-minutes: 10 + timeout-minutes: 15 strategy: fail-fast: false matrix: include: - - destination: "OS=17.5,name=iPhone 15 Pro" - name: "Test: iOS 17.6" - testPlan: "iOS" - xcode: "Xcode_15.4" - runsOn: macos-14 - - destination: "OS=16.4,name=iPhone 14 Pro" - name: "Test: iOS 16.4" - testPlan: "iOS" - xcode: "Xcode_14.3.1" - runsOn: macOS-13 - - destination: "OS=15.5,name=iPhone 13 Pro" - name: "Test: iOS 15.5" - testPlan: "iOS" - xcode: "Xcode_13.4.1" - runsOn: macOS-12 + - platform: "iOS" + osFilter: "SimRuntime\\.iOS" + devicePrefix: "iPhone" + runsOn: macos-26 + name: "Test: iOS, Xcode 26" + - platform: "iOS" + osFilter: "SimRuntime\\.iOS" + devicePrefix: "iPhone" + runsOn: macos-15 + name: "Test: iOS, Xcode 16.4" + - platform: "tvOS" + osFilter: "SimRuntime\\.tvOS" + devicePrefix: "Apple TV" + runsOn: macos-26 + name: "Test: tvOS, Xcode 26" + - platform: "tvOS" + osFilter: "SimRuntime\\.tvOS" + devicePrefix: "Apple TV" + runsOn: macos-15 + name: "Test: tvOS, Xcode 16.4" + - platform: "watchOS" + osFilter: "SimRuntime\\.watchOS" + devicePrefix: "Apple Watch" + runsOn: macos-26 + name: "Test: watchOS, Xcode 26" + - platform: "watchOS" + osFilter: "SimRuntime\\.watchOS" + devicePrefix: "Apple Watch" + runsOn: macos-15 + name: "Test: watchOS, Xcode 16.4" + - platform: "visionOS" + osFilter: "(xrOS|visionOS)" + devicePrefix: "Apple Vision" + runsOn: macos-26 + name: "Test: visionOS, Xcode 26" steps: - uses: actions/checkout@v4 - name: ${{ matrix.name }} - run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -project "${{ env.project_name }}.xcodeproj" -scheme ${{ env.scheme }} -destination "${{ matrix.destination }}" clean test 2>&1 | xcbeautify --renderer github-actions - needs: lint_code - - test_tvOS: - name: ${{ matrix.name }} - runs-on: ${{ matrix.runsOn }} - env: - DEVELOPER_DIR: /Applications/${{ matrix.xcode }}.app/Contents/Developer - timeout-minutes: 10 - strategy: - fail-fast: false - matrix: - include: - - destination: "OS=17.5,name=Apple TV" - name: "Test: tvOS 17.5" - testPlan: "tvOS" - xcode: "Xcode_15.4" - runsOn: macos-14 - - destination: "OS=16.4,name=Apple TV" - name: "Test: tvOS 16.4" - testPlan: "tvOS" - xcode: "Xcode_14.3.1" - runsOn: macOS-13 - - destination: "OS=15.4,name=Apple TV" - name: "Test: tvOS 15.4" - testPlan: "tvOS" - xcode: "Xcode_13.4.1" - runsOn: macOS-12 - steps: - - uses: actions/checkout@v4 - - name: ${{ matrix.name }} - run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -project "${{ env.project_name }}.xcodeproj" -scheme ${{ env.scheme }} -destination "${{ matrix.destination }}" clean test 2>&1 | xcbeautify --renderer github-actions - needs: lint_code - - test_visionOS: - name: ${{ matrix.name }} - runs-on: ${{ matrix.runsOn }} - env: - DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" - timeout-minutes: 10 - strategy: - fail-fast: false - matrix: - include: - - destination: "OS=1.2,name=Apple Vision Pro" - name: "Test: visionOS 1.2" - testPlan: "visionOS" - xcode: "Xcode_15.4" - runsOn: macos-14 - steps: - - uses: actions/checkout@v4 - - name: ${{ matrix.name }} - run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -project "${{ env.project_name }}.xcodeproj" -scheme "${{ env.scheme }}" -destination "${{ matrix.destination }}" clean test 2>&1 | xcbeautify --renderer github-actions - needs: lint_code - - test_watchOS: - name: ${{ matrix.name }} - runs-on: ${{ matrix.runsOn }} - env: - DEVELOPER_DIR: /Applications/${{ matrix.xcode }}.app/Contents/Developer - timeout-minutes: 10 - strategy: - fail-fast: false - matrix: - include: - - destination: "OS=10.5,name=Apple Watch Series 9 (45mm)" - name: "Test: watchOS 10.5" - testPlan: "watchOS" - xcode: "Xcode_15.4" - runsOn: macos-14 - - destination: "OS=9.4,name=Apple Watch Series 8 (45mm)" - name: "Test: watchOS 9.4" - testPlan: "watchOS" - xcode: "Xcode_14.3.1" - runsOn: macOS-13 - - destination: "OS=8.5,name=Apple Watch Series 7 - 45mm" - name: "Test: watchOS 8.5" - testPlan: "watchOS" - xcode: "Xcode_13.4.1" - runsOn: macOS-12 - steps: - - uses: actions/checkout@v4 - - name: ${{ matrix.name }} - run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -project "${{ env.project_name }}.xcodeproj" -scheme ${{ env.scheme }} -destination "${{ matrix.destination }}" clean test 2>&1 | xcbeautify --renderer github-actions + run: | + set -o pipefail + device=$(xcrun simctl list devices available -j \ + | jq -r --arg os "${{ matrix.osFilter }}" '.devices | to_entries[] | select(.key | test($os)) | .value[] | .name' \ + | grep -F "${{ matrix.devicePrefix }}" | head -1) + if [ -z "$device" ]; then + echo "::error::No available ${{ matrix.platform }} simulator matching '${{ matrix.devicePrefix }}' on this image." + xcrun simctl list devices available + exit 1 + fi + echo "Testing on simulator: $device" + env NSUnbufferedIO=YES xcodebuild -project "${{ env.project_name }}.xcodeproj" -scheme ${{ env.scheme }} -destination "platform=${{ matrix.platform }} Simulator,name=$device" clean test 2>&1 | xcbeautify --renderer github-actions needs: lint_code SPM: name: ${{ matrix.name }} runs-on: ${{ matrix.runsOn }} - env: - DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" timeout-minutes: 10 strategy: fail-fast: false matrix: include: - - xcode: "Xcode_15.4" - runsOn: macOS-14 - name: "SPM: macOS 14, Swift 5.10" - outputFilter: xcbeautify --renderer github-actions - - xcode: "Xcode_15.2" - runsOn: macOS-14 - name: "SPM: macOS 14, Swift 5.9.2" - outputFilter: xcbeautify --renderer github-actions - - xcode: "Xcode_14.3.1" - runsOn: macOS-13 - name: "SPM: macOS 13, Swift 5.8.0" - outputFilter: xcbeautify --renderer github-actions - - xcode: "Xcode_14.2" - runsOn: macOS-12 - name: "SPM: macOS 12, Swift 5.7.2" - outputFilter: xcpretty - - xcode: "Xcode_13.3.1" - runsOn: macOS-12 - name: "SPM: macOS 12, Swift 5.6.0" - outputFilter: xcpretty - - xcode: "Xcode_13.2.1" - runsOn: macOS-12 - name: "SPM: macOS 12, Swift 5.5.2" - outputFilter: xcpretty + - runsOn: macos-26 + name: "SPM: macOS 26, Xcode 26" + - runsOn: macos-15 + name: "SPM: macOS 15, Xcode 16.4" steps: - uses: actions/checkout@v4 - name: Test SPM - run: swift test -c debug 2>&1 | ${{ matrix.outputFilter }} - needs: [test_macOS, test_Catalyst, test_iOS, test_tvOS, test_watchOS, test_visionOS] - - carthage: - name: ${{ matrix.name }} - runs-on: ${{ matrix.runsOn }} - env: - DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" - strategy: - fail-fast: true - matrix: - include: - - xcode: "Xcode_15.4" - runsOn: macOS-14 - name: "Carthage: macOS" - platform: "macos" - - xcode: "Xcode_15.4" - runsOn: macOS-14 - name: "Carthage: iOS" - platform: "ios" - - xcode: "Xcode_15.4" - runsOn: macOS-14 - name: "Carthage: tvOS" - platform: "tvos" - - xcode: "Xcode_15.4" - runsOn: macOS-14 - name: "Carthage: watchOS" - platform: "watchos" - - xcode: "Xcode_15.4" - runsOn: macOS-14 - name: "Carthage: visionOS" - platform: "visionos" - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Build Carthage for ${{ matrix.platform }} - run: | - carthage build \ - --no-skip-current \ - --platform ${{ matrix.platform }} \ - --use-xcframeworks \ - --cache-builds - needs: SPM + run: swift test -c debug 2>&1 | xcbeautify --renderer github-actions + needs: [test_macOS, test_Catalyst, test_simulators] + ### Carthage builds are no longer verified by CI (Carthage is in maintenance mode; + ### compatibility is kept on a best-effort basis via the Xcode project). + ### CocoaPods linting is reduced to macOS/iOS Release: the trunk becomes read-only + ### on 2026-12-02 and this job will be removed after that date. lint_cocoapods: name: "Pods: ${{ matrix.platform }}, ${{ matrix.configuration }}" - runs-on: ${{ matrix.runsOn }} - env: - DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" + runs-on: macos-26 strategy: fail-fast: true matrix: - include: - - xcode: "Xcode_15.4" - runsOn: macOS-14 - platform: "macos" - configuration: "Release" - name: "Pods: macOS, Release" - - xcode: "Xcode_15.4" - runsOn: macOS-14 - platform: "macos" - configuration: "Debug" - name: "Pods: macOS, Debug" - - xcode: "Xcode_15.4" - runsOn: macOS-14 - platform: "ios" - configuration: "Release" - name: "Pods: iOS, Release" - - xcode: "Xcode_15.4" - runsOn: macOS-14 - platform: "ios" - configuration: "Debug" - name: "Pods: iOS, Debug" - - xcode: "Xcode_15.4" - runsOn: macOS-14 - platform: "tvos" - configuration: "Release" - name: "Pods: tvOS, Release" - - xcode: "Xcode_15.4" - runsOn: macOS-14 - platform: "tvos" - configuration: "Debug" - name: "Pods: tvOS, Debug" - - xcode: "Xcode_15.4" - runsOn: macOS-14 - platform: "watchos" - configuration: "Release" - name: "Pods: watchOS, Release" - - xcode: "Xcode_15.4" - runsOn: macOS-14 - platform: "watchos" - configuration: "Debug" - name: "Pods: watchOS, Debug" - - xcode: "Xcode_15.4" - runsOn: macOS-14 - platform: "visionos" - configuration: "Release" - name: "Pods: visionOS, Release" - - xcode: "Xcode_15.4" - runsOn: macOS-14 - platform: "visionos" - configuration: "Debug" - name: "Pods: visionOS, Debug" + platform: [macos, ios] + configuration: [Release] steps: - - name: Update CocoaPods - if: ${{ matrix.platform == 'visionos' }} - run: gem install cocoapods - name: Checkout uses: actions/checkout@v4 - name: Pod lib lint @@ -400,70 +195,20 @@ jobs: --configuration=${{ matrix.configuration }} \ --platforms=${{ matrix.platform }} \ ${{ env.podspec_name }}.podspec - needs: carthage - - update_tag: - if: github.ref == 'refs/heads/main' - runs-on: macOS-latest - steps: - - name: Checkout branch - uses: actions/checkout@v4 - with: - fetch-depth: 2 - - - name: Extract version and add tag - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - ### Enable error handling and exit the script on pipe failures - set -eo pipefail - ### Retrieve build settings and execute a command to filter MARKETING_VERSION - current_version=$(grep -m1 'MARKETING_VERSION' "${{ env.project_name }}.xcodeproj/project.pbxproj" | sed 's/.*= //;s/;//') - echo "Current version: $current_version" - ### If the current version is found - if [[ $current_version =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - ### If the tag exists, delete it from both local and remote - git fetch --tags - if git tag -l | grep -q "$current_version"; then - git tag -d "$current_version" - git push origin ":refs/tags/$current_version" - fi - ### Create a new tag for the current version and push it to the remote repository - git tag "$current_version" - git push origin "$current_version" - else - ### If the version could not be retrieved, display an error message - echo "Error: Could not retrieve the version." - fi - needs: lint_cocoapods + needs: SPM - push_cocoapods: - if: github.ref == 'refs/heads/main' - runs-on: macOS-latest + ### Aggregate gate used as the sole required status check on main. + ### Matrix job names change with Xcode versions; this name must stay stable. + ci_success: + name: CI Success + runs-on: ubuntu-latest + if: always() + needs: [lint_code, test_macOS, test_Catalyst, test_simulators, SPM, lint_cocoapods] steps: - - name: Checkout branch - uses: actions/checkout@v4 - - name: Publish to cocoapods - env: - COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }} + - name: Check all jobs succeeded run: | - ### Enable error handling and exit the script on pipe failures - set -eo pipefail - ### Retrieve the current version from the project file - current_version=$(grep -m1 'MARKETING_VERSION' "${{ env.project_name }}.xcodeproj/project.pbxproj" | sed 's/.*= //;s/;//') - echo "Current version: $current_version" - ### Check if the current version is a valid semantic version - if [[ ! "$current_version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "Error: Invalid version number" + if [[ "${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}" == "true" ]]; then + echo "One or more required jobs failed or were cancelled." exit 1 fi - ### Check if the current version already exists in the CocoaPods trunk - if pod trunk info ${{ env.podspec_name }} | grep -q "$current_version"; then - echo "Start deleting $current_version" - ### Delete the existing version from the CocoaPods trunk - echo "y" | pod trunk delete ${{ env.podspec_name }} $current_version || true - fi - echo "Start pushing $current_version" - ### Push the new version to the CocoaPods trunk - pod trunk push ${{ env.podspec_name }}.podspec --allow-warnings - needs: update_tag + echo "All required jobs succeeded." diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..36fb4ef --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,44 @@ +# Publishes the library to CocoaPods trunk when a version tag is pushed. +# Tags are immutable: pushing a tag for a version that is already published fails +# instead of replacing the existing release. Bump the version and tag again instead. + +name: Release + +on: + push: + tags: + - "[0-9]+.[0-9]+.[0-9]+" + +env: + project_name: Punycode + podspec_name: Punycode + +jobs: + publish_cocoapods: + name: Publish to CocoaPods + runs-on: macos-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Verify tag matches project version + run: | + set -eo pipefail + current_version=$(grep -m1 'MARKETING_VERSION' "${{ env.project_name }}.xcodeproj/project.pbxproj" | sed 's/.*= //;s/;//') + echo "Project version: ${current_version} / Tag: ${GITHUB_REF_NAME}" + if [[ "${current_version}" != "${GITHUB_REF_NAME}" ]]; then + echo "Error: Tag (${GITHUB_REF_NAME}) does not match MARKETING_VERSION (${current_version})." + exit 1 + fi + + - name: Publish to CocoaPods trunk + env: + COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }} + run: | + set -eo pipefail + ### Never republish an existing version; released versions are immutable. + if pod trunk info ${{ env.podspec_name }} | grep -q "${GITHUB_REF_NAME}"; then + echo "Error: Version ${GITHUB_REF_NAME} already exists on CocoaPods trunk. Bump the version instead of republishing." + exit 1 + fi + pod trunk push ${{ env.podspec_name }}.podspec --allow-warnings diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..7b35b2d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,59 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Overview + +PunycodeSwift — a pure Swift library (no dependencies) that encodes/decodes Punycode (RFC 3492) and IDNA hostnames via `String` / `Substring` extensions. Distributed primarily via SPM. Carthage compatibility is best-effort (not CI-verified); CocoaPods distribution (pod name: `Punycode`) ends when the trunk becomes read-only on 2026-12-02. + +## Commands + +```bash +# Build and test (SPM) +swift build +swift test + +# Run a single test method +swift test --filter PunycodeTests/ + +# Lint (enforced in CI via swift-format, not SwiftLint) +swift-format lint --ignore-unparsable-files --configuration .swift-format --recursive Sources Tests + +# Fastlane lanes (require `bundle install` first; Ruby/Python managed by mise) +bundle exec fastlane tests # xcodebuild tests on all platforms (macOS/iOS/tvOS/watchOS/visionOS) + slather coverage +bundle exec fastlane lint_swift # swift-format lint +bundle exec fastlane build_spm # swift build + test +bundle exec fastlane build_carthage # Carthage builds per platform +bundle exec fastlane lint_cocoapods # pod lib lint +bundle exec fastlane gen_docs # jazzy docs into docs/ +bundle exec fastlane set_version # prompt for version; syncs pbxproj + podspec + .jazzy.yml +bundle exec fastlane bump_version # patch/minor/major bump; same sync + +# Interactive job menu (fzf) +./run.sh +``` + +Xcode-based test for a specific platform (what CI runs): + +```bash +xcodebuild -project Punycode.xcodeproj -scheme Punycode -destination "platform=macOS" clean test +``` + +## Architecture + +Three source files in `Sources/`: + +- `Punycode.swift` — `Puny` class, the entire RFC 3492 implementation (`encodePunycode` / `decodePunycode`) plus IDNA hostname handling (`encodeIDNA` / `decodedIDNA`, which split on `.` and apply the `xn--` ACE prefix per label). +- `Extensions.swift` — the public API surface: computed vars `punycodeEncoded`, `punycodeDecoded`, `idnaEncoded`, `idnaDecoded` on `Substring` (primary) and `String` (forwards to `Substring`). All return `String?`, nil on invalid input. +- `Helpers.swift` — internal utilities (`Substring.lastIndex(of:)`, `UnicodeScalar.isValid`). + +Tests live in `Tests/PunycodeTests.swift` (single XCTest file; SPM test target name is `PunycodeSwiftTests`). + +## Versioning and release + +- Single source of truth for the version: `MARKETING_VERSION` in `Punycode.xcodeproj/project.pbxproj`. Fastlane and CI both read it. Never edit versions by hand — use `fastlane set_version` / `bump_version`, which also sync `Punycode.podspec` and `.jazzy.yml`. +- Branch flow: work on `develop`, PR into `main`. +- CI (`.github/workflows/main.yml`) runs on push and pull request to `main`/`develop`: lint → per-platform xcodebuild tests (simulator devices resolved at runtime via `simctl`) → SPM → pod lib lint. It does not release. Carthage builds are not CI-verified. +- After 2026-12-02 (CocoaPods trunk read-only): remove the `lint_cocoapods` job from `main.yml` and the pod push from `release.yml`. +- Releasing is a separate, explicit step: push a bare version tag (e.g. `3.0.1`) matching `MARKETING_VERSION`. `.github/workflows/release.yml` then verifies the tag against the project version and pushes to CocoaPods trunk. Use `./run.sh` → "Github - Update tag" to create the tag. +- Version tags are immutable: both the release workflow and `run.sh` fail if the tag or trunk version already exists. To re-release, bump the version — never delete/re-push a tag. diff --git a/Gemfile.lock b/Gemfile.lock index 36d7dc0..475a59e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,53 +1,56 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.7) - base64 - nkf - rexml - activesupport (7.2.1) + CFPropertyList (3.0.8) + abbrev (0.1.2) + activesupport (7.2.3.2) base64 + benchmark (>= 0.3) 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) + minitest (>= 5.1, < 6) securerandom (>= 0.3) tzinfo (~> 2.0, >= 2.0.5) - addressable (2.8.7) - public_suffix (>= 2.0.2, < 7.0) + addressable (2.9.0) + public_suffix (>= 2.0.2, < 8.0) algoliasearch (1.27.5) httpclient (~> 2.8, >= 2.8.3) json (>= 1.5.1) artifactory (3.0.17) atomos (0.1.3) - aws-eventstream (1.3.0) - aws-partitions (1.968.0) - aws-sdk-core (3.201.5) + aws-eventstream (1.4.0) + aws-partitions (1.1281.0) + aws-sdk-core (3.254.1) aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.651.0) + aws-partitions (~> 1, >= 1.992.0) aws-sigv4 (~> 1.9) + base64 + bigdecimal jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.88.0) - aws-sdk-core (~> 3, >= 3.201.0) + logger + aws-sdk-kms (1.130.0) + aws-sdk-core (~> 3, >= 3.254.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.159.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-s3 (1.229.0) + aws-sdk-core (~> 3, >= 3.254.1) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) - aws-sigv4 (1.9.1) + aws-sigv4 (1.12.1) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) - base64 (0.2.0) - bigdecimal (3.1.8) + base64 (0.3.0) + benchmark (0.5.0) + bigdecimal (4.1.2) claide (1.1.0) - clamp (1.3.2) - cocoapods (1.15.2) + clamp (1.5.2) + cocoapods (1.17.0) addressable (~> 2.8) claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.15.2) + cocoapods-core (= 1.17.0) cocoapods-deintegrate (>= 1.0.3, < 2.0) cocoapods-downloader (>= 2.1, < 3.0) cocoapods-plugins (>= 1.0.0, < 2.0) @@ -55,15 +58,14 @@ GEM cocoapods-trunk (>= 1.6.0, < 2.0) cocoapods-try (>= 1.1.0, < 2.0) colored2 (~> 3.1) - escape (~> 0.0.4) fourflusher (>= 2.3.0, < 3.0) gh_inspector (~> 1.0) molinillo (~> 0.8.0) nap (~> 1.0) - ruby-macho (>= 2.3.0, < 3.0) - xcodeproj (>= 1.23.0, < 2.0) + ruby-macho (~> 4.1.0) + xcodeproj (>= 1.28.1, < 2.0) cocoapods-clean (0.0.1) - cocoapods-core (1.15.2) + cocoapods-core (1.17.0) activesupport (>= 5.0, < 8) addressable (~> 2.8) algoliasearch (~> 1.0) @@ -86,147 +88,166 @@ GEM colored2 (3.1.2) commander (4.6.0) highline (~> 2.0.0) - concurrent-ruby (1.3.4) - connection_pool (2.4.1) + concurrent-ruby (1.3.8) + connection_pool (3.0.2) + csv (3.3.6) declarative (0.0.20) - digest-crc (0.6.5) + digest-crc (0.7.0) rake (>= 12.0.0, < 14.0.0) domain_name (0.6.20240107) dotenv (2.8.1) - drb (2.2.1) + drb (2.2.3) emoji_regex (3.2.3) - escape (0.0.4) - ethon (0.16.0) + erb (6.0.7) + ethon (0.18.0) ffi (>= 1.15.0) - 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) + logger + excon (1.6.0) + logger + faraday (2.14.3) + faraday-net_http (>= 2.0, < 3.5) + json + logger + faraday-cookie_jar (0.0.8) 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) + http-cookie (>= 1.0.0) + faraday-follow_redirects (0.5.0) + faraday (>= 1, < 3) + faraday-multipart (1.2.0) + multipart-post (~> 2.0) + faraday-net_http (3.4.4) + net-http (~> 0.5) + faraday-retry (2.4.0) + faraday (~> 2.0) + fastimage (2.4.1) + fastlane (2.238.0) + CFPropertyList (>= 2.3, < 5.0.0) + abbrev (~> 0.1) + addressable (>= 2.9.0, < 3.0.0) artifactory (~> 3.0) - aws-sdk-s3 (~> 1.0) + aws-sdk-s3 (~> 1.197) babosa (>= 1.0.3, < 2.0.0) - bundler (>= 1.12.0, < 3.0.0) + base64 (~> 0.2) + benchmark (>= 0.1.0) + bundler (>= 2.4.0, < 5.0.0) colored (~> 1.2) commander (~> 4.6) + csv (~> 3.3) 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) + excon (>= 0.71.0, < 2.0.0) + faraday (~> 2.7) + faraday-cookie_jar (~> 0.0.8) + faraday-follow_redirects (~> 0.3) + faraday-multipart (~> 1.0) + faraday-retry (~> 2.0) fastimage (>= 2.1.0, < 3.0.0) + fastlane-sirp (>= 1.1.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-env (>= 1.6.0, < 2.3.0) google-cloud-storage (~> 1.31) highline (~> 2.0) http-cookie (~> 1.0.5) + irb (>= 1.8) json (< 3.0.0) - jwt (>= 2.1.0, < 3) + jwt (>= 2.10.3, < 4) + logger (>= 1.6, < 2.0) mini_magick (>= 4.9.4, < 5.0.0) + multi_json (~> 1.12) multipart-post (>= 2.0.0, < 3.0.0) + mutex_m (~> 0.3) naturally (~> 2.2) + nkf (~> 0.2) optparse (>= 0.1.1, < 1.0.0) + ostruct (>= 0.1.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) + terminal-table (~> 4) 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 (~> 0.4.1) xcpretty-travis-formatter (>= 0.0.3, < 2.0.0) - fastlane-plugin-versioning (0.6.0) - ffi (1.17.0-aarch64-linux-gnu) - ffi (1.17.0-aarch64-linux-musl) - ffi (1.17.0-arm-linux-gnu) - ffi (1.17.0-arm-linux-musl) - ffi (1.17.0-arm64-darwin) - ffi (1.17.0-x86-linux-gnu) - ffi (1.17.0-x86-linux-musl) - ffi (1.17.0-x86_64-darwin) - ffi (1.17.0-x86_64-linux-gnu) - ffi (1.17.0-x86_64-linux-musl) + fastlane-plugin-versioning (0.7.1) + fastlane-sirp (1.1.0) + ffi (1.17.4-aarch64-linux-gnu) + ffi (1.17.4-aarch64-linux-musl) + ffi (1.17.4-arm-linux-gnu) + ffi (1.17.4-arm-linux-musl) + ffi (1.17.4-arm64-darwin) + ffi (1.17.4-x86-linux-gnu) + ffi (1.17.4-x86-linux-musl) + ffi (1.17.4-x86_64-darwin) + ffi (1.17.4-x86_64-linux-gnu) + ffi (1.17.4-x86_64-linux-musl) fourflusher (2.3.1) fuzzy_match (2.0.4) 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) + google-apis-androidpublisher_v3 (0.106.0) + google-apis-core (>= 0.15.0, < 2.a) + google-apis-core (1.2.5) + addressable (~> 2.9) + faraday (~> 2.13) + faraday-follow_redirects (~> 0.3) + googleauth (~> 1.14) + mini_mime (~> 1.1) + multi_json (~> 1.11) 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) + retriable (>= 3.1, < 5.0) + google-apis-iamcredentials_v1 (0.28.0) + google-apis-core (>= 0.15.0, < 2.a) + google-apis-playcustomapp_v1 (0.18.0) + google-apis-core (>= 0.15.0, < 2.a) + google-apis-storage_v1 (0.66.0) + google-apis-core (>= 0.15.0, < 2.a) + google-cloud-core (1.9.0) 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) + google-cloud-env (2.2.2) + base64 (~> 0.2) + faraday (>= 1.0, < 3.a) + google-cloud-errors (1.7.0) + google-cloud-storage (1.62.0) addressable (~> 2.8) digest-crc (~> 0.4) - google-apis-iamcredentials_v1 (~> 0.1) - google-apis-storage_v1 (~> 0.31.0) + google-apis-core (>= 0.18, < 2) + google-apis-iamcredentials_v1 (~> 0.18) + google-apis-storage_v1 (>= 0.42) google-cloud-core (~> 1.6) - googleauth (>= 0.16.2, < 2.a) + googleauth (~> 1.9) mini_mime (~> 1.0) - googleauth (1.8.1) - faraday (>= 0.17.3, < 3.a) - jwt (>= 1.4, < 3.0) - multi_json (~> 1.11) + google-logging-utils (0.2.0) + googleauth (1.17.3) + faraday (>= 1.0, < 3.a) + google-cloud-env (~> 2.2) + google-logging-utils (~> 0.1) + jwt (>= 1.4, < 4.0) os (>= 0.9, < 2.0) + pstore (~> 0.1) signet (>= 0.16, < 2.a) highline (2.0.3) - http-cookie (1.0.7) + http-cookie (1.0.8) domain_name (~> 0.5) - httpclient (2.8.3) - i18n (1.14.5) + httpclient (2.9.0) + mutex_m + i18n (1.15.2) concurrent-ruby (~> 1.0) - jazzy (0.15.1) + io-console (0.9.2) + irb (1.18.0) + pp (>= 0.6.0) + prism (>= 1.3.0) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + jazzy (0.15.4) + activesupport (>= 5.0, < 8) cocoapods (~> 1.5) + logger mustache (~> 1.1) open4 (~> 1.3) redcarpet (~> 3.4) @@ -236,105 +257,135 @@ GEM sqlite3 (~> 1.3) xcinvoke (~> 0.3.0) jmespath (1.6.2) - json (2.7.2) - jwt (2.8.2) + json (2.21.2) + jwt (3.2.0) base64 liferaft (0.0.6) - logger (1.6.0) + logger (1.7.0) mini_magick (4.13.2) mini_mime (1.1.5) - minitest (5.25.1) + mini_portile2 (2.8.9) + minitest (5.27.0) molinillo (0.8.0) - multi_json (1.15.0) + multi_json (1.21.1) multipart-post (2.4.1) - mustache (1.1.1) - nanaimo (0.3.0) + mustache (1.1.2) + mutex_m (0.3.0) + nanaimo (0.4.0) nap (1.1.0) - naturally (2.2.1) + naturally (2.3.0) + net-http (0.9.1) + uri (>= 0.11.1) netrc (0.11.0) - nkf (0.2.0) - nokogiri (1.16.7-aarch64-linux) + nkf (0.3.0) + nokogiri (1.19.4) + mini_portile2 (~> 2.8.2) + racc (~> 1.4) + nokogiri (1.19.4-aarch64-linux-gnu) + racc (~> 1.4) + nokogiri (1.19.4-aarch64-linux-musl) racc (~> 1.4) - nokogiri (1.16.7-arm-linux) + nokogiri (1.19.4-arm-linux-gnu) racc (~> 1.4) - nokogiri (1.16.7-arm64-darwin) + nokogiri (1.19.4-arm-linux-musl) racc (~> 1.4) - nokogiri (1.16.7-x86-linux) + nokogiri (1.19.4-arm64-darwin) racc (~> 1.4) - nokogiri (1.16.7-x86_64-darwin) + nokogiri (1.19.4-x86_64-darwin) racc (~> 1.4) - nokogiri (1.16.7-x86_64-linux) + nokogiri (1.19.4-x86_64-linux-gnu) + racc (~> 1.4) + nokogiri (1.19.4-x86_64-linux-musl) racc (~> 1.4) open4 (1.3.4) - optparse (0.5.0) + optparse (0.8.1) os (1.1.4) - plist (3.7.1) + ostruct (0.6.3) + plist (3.7.2) + pp (0.6.4) + prettyprint + prettyprint (0.2.0) + prism (1.9.0) + pstore (0.2.1) public_suffix (4.0.7) racc (1.8.1) - rake (13.2.1) - redcarpet (3.6.0) + rake (13.4.2) + rbs (4.1.3) + logger + prism (>= 1.6.0) + tsort + rdoc (8.0.0) + erb + prism (>= 1.6.0) + rbs (>= 4.0.0) + tsort + redcarpet (3.6.1) + reline (0.7.0) + io-console (~> 0.5) 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.6) - strscan - rouge (2.0.7) - ruby-macho (2.5.1) - ruby2_keywords (0.0.5) - rubyzip (2.3.2) + retriable (4.2.0) + rexml (3.4.4) + rouge (3.28.0) + ruby-macho (4.1.0) + rubyzip (2.4.1) sassc (2.4.0) ffi (~> 1.9) - securerandom (0.3.1) + securerandom (0.4.1) security (0.1.5) - signet (0.19.0) + signet (0.22.0) addressable (~> 2.8) faraday (>= 0.17.5, < 3.a) - jwt (>= 1.5, < 3.0) - multi_json (~> 1.10) + jwt (>= 1.5, < 4.0) simctl (1.6.10) CFPropertyList naturally - slather (2.8.3) + slather (2.8.5) CFPropertyList (>= 2.2, < 4) activesupport clamp (~> 1.3) nokogiri (>= 1.14.3) - xcodeproj (~> 1.21) + xcodeproj (~> 1.27) sqlite3 (1.7.3-aarch64-linux) sqlite3 (1.7.3-arm-linux) sqlite3 (1.7.3-arm64-darwin) sqlite3 (1.7.3-x86-linux) sqlite3 (1.7.3-x86_64-darwin) sqlite3 (1.7.3-x86_64-linux) - strscan (3.1.0) terminal-notifier (2.0.0) - terminal-table (3.0.2) - unicode-display_width (>= 1.1.1, < 3) + terminal-table (4.0.0) + unicode-display_width (>= 1.1.1, < 4) trailblazer-option (0.1.2) + tsort (0.2.0) tty-cursor (0.7.1) tty-screen (0.8.2) tty-spinner (0.9.3) tty-cursor (~> 0.7) - typhoeus (1.4.1) - ethon (>= 0.9.0) + typhoeus (1.6.0) + ethon (>= 0.18.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) uber (0.1.0) - unicode-display_width (2.5.0) + unicode-display_width (3.2.0) + unicode-emoji (~> 4.1) + unicode-emoji (4.2.0) + uri (1.1.1) word_wrap (1.0.0) xcinvoke (0.3.0) liferaft (~> 0.0.6) - xcodeproj (1.25.0) + xcodeproj (1.28.1) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) + base64 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) + nanaimo (~> 0.4.0) + nkf + rexml (>= 3.3.6, < 4.0) + xcpretty (0.4.1) + rouge (~> 3.28.0) xcpretty-travis-formatter (1.0.1) xcpretty (~> 0.2, >= 0.0.7) diff --git a/README.md b/README.md index ffc1b99..055127f 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,9 @@ [![Cocoapods Version](https://img.shields.io/cocoapods/v/Punycode.svg)](https://cocoapods.org/pods/Punycode) [![Cocoapods Platform](https://img.shields.io/cocoapods/p/Punycode.svg)](https://cocoadocs.org/docsets/Punycode) [![Build](https://github.com/futamura/PunycodeSwift/actions/workflows/main.yml/badge.svg)](https://github.com/futamura/PunycodeSwift/actions/workflows/main.yml) -[![codecov](https://codecov.io/gh/futamura/PunycodeSwift/branch/master/graph/badge.svg)](https://codecov.io/gh/futamura/PunycodeSwift) +[![codecov](https://codecov.io/gh/futamura/PunycodeSwift/branch/main/graph/badge.svg)](https://codecov.io/gh/futamura/PunycodeSwift) ![Language](https://img.shields.io/badge/Language-Swift%205.0-orange.svg) -![Packagist](https://img.shields.io/packagist/l/doctrine/orm.svg) +![License](https://img.shields.io/github/license/futamura/PunycodeSwift.svg) # PunycodeSwift @@ -25,6 +25,11 @@ Punycode is a representation of Unicode with the limited ASCII character subset ## Installation +Swift Package Manager is the recommended way to install PunycodeSwift. + +> [!IMPORTANT] +> The CocoaPods trunk becomes permanently read-only on December 2, 2026. Versions released after that date will be available via Swift Package Manager (and Carthage on a best-effort basis) only. If you are using CocoaPods, please migrate to Swift Package Manager. + ### Swift Package Manager Add the following to your `Package.swift` file. @@ -45,6 +50,9 @@ Add the following to your `Package.swift` file. ### Carthage +> [!NOTE] +> Carthage itself is in maintenance mode. Carthage compatibility is kept on a best-effort basis and is no longer verified by CI. + Add the following to your `Cartfile` and follow [these instructions](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application). - macOS, iOS, tvOS, watchOS, visionOS, and Swift 5 @@ -67,7 +75,10 @@ Add the following to your `Cartfile` and follow [these instructions](https://git ### CocoaPods -To integrate TLDExtract into your project, add the following to your `Podfile`. +> [!WARNING] +> CocoaPods distribution ends when the trunk becomes read-only on December 2, 2026. Existing versions will remain installable, but no new versions will be published. Please migrate to Swift Package Manager. + +To integrate PunycodeSwift into your project, add the following to your `Podfile`. - macOS, iOS, tvOS, watchOS, visionOS, and Swift 5.0 diff --git a/run.sh b/run.sh index 608cf39..aeedd92 100755 --- a/run.sh +++ b/run.sh @@ -81,11 +81,10 @@ local cocoapods_trunk_push() { echo "Error: Invalid version number" exit 1 fi - # Check if the current version already exists in the CocoaPods trunk + # Never republish an existing version; released versions are immutable if pod trunk info ${podspec_name} | grep -q "$current_version"; then - echo "Start deleting $current_version" - # Delete the existing version from the CocoaPods trunk - echo "y" | pod trunk delete ${podspec_name} $current_version || true + echo "Error: Version $current_version already exists on CocoaPods trunk. Bump the version instead of republishing." + exit 1 fi echo "Start pushing $current_version" # Push the new version to the CocoaPods trunk @@ -108,13 +107,13 @@ local github_update_tag() { echo "Current version: $current_version" # If the current version is found if [[ $current_version =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - # Check if a tag for the current version already exists - if git tag -l | grep -q "$current_version"; then - # If the tag exists, delete it from both local and remote - git tag -d "$current_version" - git push origin ":refs/tags/$current_version" + # Tags are immutable: never delete or overwrite an existing version tag + if git tag -l | grep -q "^${current_version}$"; then + echo "Error: Tag $current_version already exists. Bump the version instead of retagging." + exit 1 fi # Create a new tag for the current version and push it to the remote repository + # Pushing this tag triggers the Release workflow (CocoaPods trunk push) git tag "$current_version" git push origin "$current_version" else