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
25 changes: 25 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,27 @@ on:
branches:
- main

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

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

- name: Cache Swift packages
uses: actions/cache@v4
with:
path: .build
key: spm-${{ runner.os }}-${{ hashFiles('Package.swift') }}
restore-keys: |
spm-${{ runner.os }}-

- name: Check Swift version
run: swift --version
Expand All @@ -28,6 +42,17 @@ jobs:
- name: Build CLI
run: swift build -c release

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

- name: Version validation warning
if: steps.version-check.outcome == 'failure'
run: echo "::warning::Version validation failed. Remember to bump the version before creating a release."

test-linux:
name: Test on Linux
runs-on: ubuntu-latest
Expand Down
96 changes: 48 additions & 48 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
name: Release

on:
push:
tags:
- 'v*'
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build-macos:
Expand All @@ -12,19 +14,31 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Cache Swift packages
uses: actions/cache@v4
with:
path: .build
key: spm-${{ runner.os }}-${{ hashFiles('Package.swift') }}
restore-keys: |
spm-${{ runner.os }}-

- name: Check Swift version
run: swift --version

- name: Get version from tag
id: get_version
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
- name: Validate version
id: version
uses: ./.github/actions/validate-version
with:
check-tag-exists: 'true'

- name: Run tests
run: swift test

- name: Build release binary
run: ./scripts/build-release.sh ${{ steps.get_version.outputs.VERSION }}
run: ./scripts/build-release.sh ${{ steps.version.outputs.version }}

- name: Upload artifacts
uses: actions/upload-artifact@v4
Expand All @@ -43,71 +57,55 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Download macOS artifact
uses: actions/download-artifact@v4
with:
name: macos-universal
path: release/

- name: Get version from tag
id: get_version
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
- name: Validate version
id: version
uses: ./.github/actions/validate-version
with:
check-tag-exists: 'true'

- name: Generate release notes
id: release_notes
run: |
cat > release_notes.md << 'EOF'
# Get previous tag for changelog
PREV_TAG=$(git tag --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+' | head -n1)

# Generate changelog
echo "## What's Changed" > release_notes.md
echo "" >> release_notes.md
if [ -z "$PREV_TAG" ]; then
git log --pretty=format:"- %s" HEAD >> release_notes.md
else
git log --pretty=format:"- %s" ${PREV_TAG}..HEAD >> release_notes.md
fi
echo "" >> release_notes.md
echo "" >> release_notes.md

# Append static content
cat >> release_notes.md << 'EOF'
## Installation

### macOS (Universal Binary - x86_64 + ARM64)

Download and install:
```bash
curl -L https://github.com/${{ github.repository }}/releases/download/v${{ steps.get_version.outputs.VERSION }}/ejson-${{ steps.get_version.outputs.VERSION }}-macos-universal.tar.gz | tar xz
sudo mv ejson /usr/local/bin/
ejson --version
```

Or with wget:
```bash
wget https://github.com/${{ github.repository }}/releases/download/v${{ steps.get_version.outputs.VERSION }}/ejson-${{ steps.get_version.outputs.VERSION }}-macos-universal.tar.gz
tar xzf ejson-${{ steps.get_version.outputs.VERSION }}-macos-universal.tar.gz
curl -L https://github.com/${{ github.repository }}/releases/latest/download/ejson-macos-universal.tar.gz | tar xz
sudo mv ejson /usr/local/bin/
ejson --version
```

### Verify Checksum

```bash
# Download checksum file
curl -L https://github.com/${{ github.repository }}/releases/download/v${{ steps.get_version.outputs.VERSION }}/ejson-${{ steps.get_version.outputs.VERSION }}-macos-universal.tar.gz.sha256 -o ejson.sha256

# Verify (macOS)
curl -L https://github.com/${{ github.repository }}/releases/latest/download/ejson-macos-universal.tar.gz.sha256 -o ejson.sha256
shasum -a 256 -c ejson.sha256

# Verify (Linux)
sha256sum -c ejson.sha256
```

## Features

- 🔐 NaCl Box encryption compatible with Shopify EJSON
- 🔄 Full compatibility with Go EJSON implementation
- ⚡ Fast and native Swift implementation
- 📦 Universal macOS binary (works on both Intel and Apple Silicon)

## Usage

```bash
# Generate a keypair
ejson keygen

# Encrypt a file
ejson encrypt secrets.json

# Decrypt a file
ejson decrypt secrets.json
```

For more information, see the [README](https://github.com/${{ github.repository }}/blob/main/README.md).
Expand All @@ -117,6 +115,8 @@ jobs:
uses: diogot/gh-actions-workflows/actions/create-release@main
with:
token: ${{ secrets.GITHUB_TOKEN }}
tag: v${{ steps.version.outputs.version }}
title: v${{ steps.version.outputs.version }}
body-file: release_notes.md
files: |
release/*.tar.gz
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Package.resolved
# .swiftpm

.build/
.swiftpm/
release/

# CocoaPods
Expand Down
4 changes: 4 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

**Reference Implementation:** https://github.com/Shopify/ejson

## Before Starting a New Feature

**Remember to bump the version!** Before starting work on a new feature or bug fix, update the `VERSION` file at the project root with the new version number. This ensures the release workflow can create a new release when the changes are merged.

## Purpose

Provide a native Swift library that can:
Expand Down
11 changes: 9 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ let package = Package(
],
dependencies: [
// Dependencies declare other packages that this package depends on.
.package(url: "https://github.com/jedisct1/swift-sodium.git", from: "0.9.1")
.package(url: "https://github.com/jedisct1/swift-sodium.git", from: "0.10.0")
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
Expand All @@ -33,9 +33,16 @@ let package = Package(
]),
.executableTarget(
name: "ejson",
dependencies: ["EJSONKit"]),
dependencies: ["EJSONKit"],
plugins: [
.plugin(name: "BuildVersionPlugin")
]),
.testTarget(
name: "EJSONKitTests",
dependencies: ["EJSONKit"]),
.plugin(
name: "BuildVersionPlugin",
capability: .buildTool()
),
]
)
31 changes: 31 additions & 0 deletions Plugins/BuildVersionPlugin/plugin.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
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 [
.buildCommand(
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
"""
],
inputFiles: [versionFile],
outputFiles: [outputFile]
)
]
}
}
Loading