Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
72 changes: 72 additions & 0 deletions .github/actions/validate-version/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: Validate Version
description: Extract and validate version from VERSION file against latest git tag

inputs:
check-tag-exists:
description: Fail if the tag already exists
required: false
default: 'false'

outputs:
version:
description: The extracted version from VERSION file
value: ${{ steps.extract.outputs.version }}

runs:
using: composite
steps:
- name: Extract version from VERSION file
id: extract
shell: bash
run: |
VERSION=$(cat VERSION | tr -d '\n')
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Extracted version: $VERSION"

- name: Get latest tag
id: latest_tag
shell: bash
run: |
LATEST_TAG=$(git tag --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+' | head -n1 || echo "")
if [ -z "$LATEST_TAG" ]; then
echo "tag=" >> $GITHUB_OUTPUT
echo "No existing tags found"
else
echo "tag=${LATEST_TAG#v}" >> $GITHUB_OUTPUT
echo "Latest tag: $LATEST_TAG"
fi

- name: Check if tag exists
if: inputs.check-tag-exists == 'true'
shell: bash
run: |
VERSION="${{ steps.extract.outputs.version }}"
if git rev-parse "v$VERSION" >/dev/null 2>&1; then
echo "Error: Tag v$VERSION already exists"
exit 1
fi

- name: Validate version bump
shell: bash
run: |
VERSION="${{ steps.extract.outputs.version }}"
LATEST="${{ steps.latest_tag.outputs.tag }}"

echo "Current version: $VERSION"
echo "Latest tag: $LATEST"

if [ -z "$LATEST" ]; then
echo "No existing tags. Version $VERSION is valid."
exit 0
fi

# Check if version is greater than latest tag using sort -V
HIGHEST=$(printf '%s\n%s' "$VERSION" "$LATEST" | sort -V | tail -n1)

if [ "$HIGHEST" = "$VERSION" ] && [ "$VERSION" != "$LATEST" ]; then
echo "Version $VERSION is greater than $LATEST"
exit 0
else
echo "Error: Version $VERSION must be greater than latest tag $LATEST"
exit 1
fi
41 changes: 41 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: CI

on:
pull_request:
branches:
- dev
- main

jobs:
build-and-test:
name: Build and Test
runs-on: macos-26
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Build
run: swift build

- name: Run tests
run: swift test

- name: Validate version (main)
if: github.base_ref == 'main'
uses: ./.github/actions/validate-version
with:
check-tag-exists: 'true'

- name: Validate version (dev)
id: version-check
if: github.base_ref == 'dev'
uses: ./.github/actions/validate-version
with:
check-tag-exists: 'true'
continue-on-error: true

- name: Version validation warning
if: github.base_ref == 'dev' && steps.version-check.outcome == 'failure'
run: echo "::warning::Version validation failed. Remember to bump the version before merging to main."
33 changes: 33 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Release

on:
push:
branches:
- main

jobs:
release:
name: Build and Release
runs-on: macos-26
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Validate version
id: version
uses: ./.github/actions/validate-version
with:
check-tag-exists: 'true'

- name: Build release binary
run: swift build -c release

- name: Create release
uses: diogot/gh-actions-workflows/actions/create-release@main
with:
tag: v${{ steps.version.outputs.version }}
title: v${{ steps.version.outputs.version }}
files: .build/release/swift-outdated
token: ${{ secrets.GITHUB_TOKEN }}
7 changes: 7 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,18 @@ let package = Package(
.package(url: "https://github.com/tuist/XcodeProj.git", from: "8.0.0")
],
targets: [
.plugin(
name: "BuildVersionPlugin",
capability: .buildTool()
),
.executableTarget(
name: "swift-outdated",
dependencies: [
"SwiftOutdatedCore",
.product(name: "ArgumentParser", package: "swift-argument-parser")
],
plugins: [
.plugin(name: "BuildVersionPlugin")
]
),
.target(
Expand Down
30 changes: 30 additions & 0 deletions Plugins/BuildVersionPlugin/plugin.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import PackagePlugin
import Foundation

@main
struct BuildVersionPlugin: BuildToolPlugin {
func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] {
let versionFile = context.package.directoryURL.appending(path: "VERSION")
let outputFile = context.pluginWorkDirectoryURL.appending(path: "GeneratedVersion.swift")

return [
.prebuildCommand(
displayName: "Generate version from VERSION file",
executable: URL(fileURLWithPath: "/bin/bash"),
arguments: [
"-c",
"""
VERSION=$(cat "\(versionFile.path)" | tr -d '\\n')
cat > "\(outputFile.path)" << EOF
// Auto-generated by BuildVersionPlugin - do not edit
enum GeneratedVersion {
static let version = "$VERSION"
}
EOF
"""
],
outputFilesDirectory: context.pluginWorkDirectoryURL
)
]
}
}
2 changes: 1 addition & 1 deletion Sources/swift-outdated/SwiftOutdated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ struct SwiftOutdated: AsyncParsableCommand {
By default, searches for Package.resolved in the current directory or within
Xcode project/workspace directories.
""",
version: "1.0.0"
version: GeneratedVersion.version
)

@Flag(name: .long, help: "Output results in JSON format")
Expand Down
1 change: 1 addition & 0 deletions VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1.0.0