this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Update release scripts

+259 -97
+47 -42
.github/workflows/build-and-release.yml .github/workflows/release.yml
··· 1 - name: CI 1 + name: Release 2 2 3 3 on: 4 - release: 5 - types: [created, edited] 4 + push: 5 + tags: 6 + - 'v*' 6 7 workflow_dispatch: 7 8 inputs: 8 9 tag: ··· 19 20 20 21 jobs: 21 22 build-and-release: 22 - runs-on: macos-26 23 + runs-on: macos-latest 23 24 24 25 outputs: 25 26 archive_name: ${{ steps.create_archive.outputs.archive_name }} ··· 48 49 id: version 49 50 run: | 50 51 if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then 51 - # Manual trigger - use provided tag or latest tag 52 52 if [ -n "${{ github.event.inputs.tag }}" ]; then 53 53 TAG="${{ github.event.inputs.tag }}" 54 54 else 55 - # Get the latest tag 56 55 TAG=$(git describe --tags --abbrev=0) 57 56 fi 58 57 echo "Using tag from manual trigger: $TAG" 59 58 else 60 - # Release event - use the release tag 61 59 TAG=${GITHUB_REF#refs/tags/} 62 - echo "Using tag from release event: $TAG" 60 + echo "Using tag from push event: $TAG" 63 61 fi 64 62 65 63 VERSION=${TAG#v} ··· 298 296 exit 1 299 297 fi 300 298 301 - - name: Delete existing release asset (if re-running) 302 - if: github.event_name == 'release' 303 - continue-on-error: true 304 - run: | 305 - ASSET_NAME="${{ steps.create_archive.outputs.archive_name }}" 306 - echo "🗑️ Checking for existing asset: $ASSET_NAME" 307 - 308 - # Get the release ID 309 - RELEASE_ID=$(gh api repos/${{ github.repository }}/releases/tags/${{ steps.version.outputs.tag }} --jq '.id') 310 - 311 - # Try to delete existing asset with same name 312 - gh api repos/${{ github.repository }}/releases/$RELEASE_ID/assets --jq '.[] | select(.name == "'"$ASSET_NAME"'") | .id' | while read asset_id; do 313 - if [ -n "$asset_id" ]; then 314 - echo "🗑️ Deleting existing asset ID: $asset_id" 315 - gh api --method DELETE repos/${{ github.repository }}/releases/assets/$asset_id 316 - echo "✅ Existing asset deleted" 317 - fi 318 - done 319 - env: 320 - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 321 - 322 - - name: Upload release asset 323 - if: github.event_name == 'release' 324 - uses: actions/upload-release-asset@v1 325 - env: 326 - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 327 - with: 328 - upload_url: ${{ github.event.release.upload_url }} 329 - asset_path: ${{ steps.create_archive.outputs.archive_name }} 330 - asset_name: ${{ steps.create_archive.outputs.archive_name }} 331 - asset_content_type: application/gzip 332 - 333 299 - name: Upload build artifact (manual trigger) 334 300 if: github.event_name == 'workflow_dispatch' 335 301 uses: actions/upload-artifact@v4 ··· 338 304 path: ${{ steps.create_archive.outputs.archive_name }} 339 305 retention-days: 30 340 306 307 + - name: Read tag release notes 308 + if: github.event_name == 'push' 309 + id: release_notes 310 + run: | 311 + TAG="${{ steps.version.outputs.tag }}" 312 + git fetch --tags --force 313 + NOTES=$(git tag -l --format='%(contents)' "$TAG") 314 + printf '%s\n' "$NOTES" > release-notes.md 315 + echo "file=release-notes.md" >> $GITHUB_OUTPUT 316 + 317 + - name: Create GitHub release 318 + if: github.event_name == 'push' 319 + env: 320 + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 321 + run: | 322 + TAG="${{ steps.version.outputs.tag }}" 323 + TITLE="Release $TAG" 324 + NOTES_FILE="${{ steps.release_notes.outputs.file }}" 325 + VERSION="${{ steps.version.outputs.version }}" 326 + 327 + if [[ "$VERSION" == *"-"* ]]; then 328 + PRERELEASE_FLAG="--prerelease" 329 + else 330 + PRERELEASE_FLAG="" 331 + fi 332 + 333 + if gh release view "$TAG" >/dev/null 2>&1; then 334 + echo "🗑️ Existing release found for $TAG. Deleting..." 335 + gh release delete "$TAG" --yes 336 + fi 337 + 338 + gh release create "$TAG" \ 339 + --title "$TITLE" \ 340 + --notes-file "$NOTES_FILE" \ 341 + $PRERELEASE_FLAG \ 342 + "${{ steps.create_archive.outputs.archive_name }}" 343 + 344 + echo "✅ Release $TAG created." 345 + 341 346 - name: Update Homebrew tap 342 347 if: | 343 - (github.event_name == 'release' && !github.event.release.prerelease) || 348 + (github.event_name == 'push' && !contains(steps.version.outputs.version, '-')) || 344 349 (github.event_name == 'workflow_dispatch') 345 350 env: 346 351 HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} ··· 390 395 echo "🧹 Cleaning up sensitive files..." 391 396 rm -rf keys/ 392 397 rm -f "$RUNNER_TEMP"/*.p12 393 - echo "✅ Cleanup completed" 398 + echo "✅ Cleanup completed"
+1 -1
README.md
··· 2 2 3 3 AXe is a comprehensive CLI tool for interacting with iOS Simulators using Apple's Accessibility APIs and HID (Human Interface Device) functionality. 4 4 5 - [![CI](https://github.com/cameroncooke/AXe/actions/workflows/build-and-release.yml/badge.svg)](https://github.com/cameroncooke/AXe/actions/workflows/build-and-release.yml) 5 + [![CI](https://github.com/cameroncooke/AXe/actions/workflows/release.yml/badge.svg)](https://github.com/cameroncooke/AXe/actions/workflows/release.yml) 6 6 [![Licence: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 7 7 8 8 ![AxeDemo](https://github.com/user-attachments/assets/9eafa5d5-3cef-4e39-82c5-7b9d41fe4548)
+1 -1
scripts/build.sh
··· 102 102 print_info "Project: ${project_file}" 103 103 104 104 invoke_xcodebuild \ 105 - -quiet \ 106 105 -project "${project_file}" \ 107 106 -scheme "${scheme_name}" \ 108 107 -sdk "${FRAMEWORK_SDK}" \ 108 + -destination "generic/platform=macOS" \ 109 109 -configuration "${FRAMEWORK_CONFIGURATION}" \ 110 110 -derivedDataPath "${DERIVED_DATA_PATH}" \ 111 111 build \
+210 -53
scripts/create-release.sh
··· 1 1 #!/bin/bash 2 + set -euo pipefail 3 + 4 + WORKFLOW_NAME="Release" 5 + WORKFLOW_FILE="release.yml" 6 + WORKFLOW_IDENTIFIER="$WORKFLOW_FILE" 7 + 8 + log_info() { printf '\n🔷 %s\n' "$1"; } 9 + log_error() { printf '❌ %s\n' "$1" >&2; exit 1; } 10 + log_success() { printf '\n✅ %s\n' "$1"; } 2 11 3 - set -e 12 + usage() { 13 + cat <<'USAGE' 14 + AXe Release Helper 4 15 5 - # --- Helper Functions --- 6 - log_info() { echo ""; echo "🔷 $1"; } 7 - log_error() { echo "❌ $1"; exit 1; } 8 - log_success() { echo "✅ $1"; } 16 + Usage: scripts/create-release.sh [VERSION|major|minor|patch] [--notes-file FILE] [--dry-run] 17 + scripts/create-release.sh --help 9 18 10 - # --- Validation --- 11 - if ! command -v gh &> /dev/null; then 12 - log_error "GitHub CLI (gh) not found. Please install it first: brew install gh" 13 - fi 19 + Arguments: 20 + VERSION Explicit semantic version (e.g. 1.4.0 or 1.5.0-beta.1) 21 + major|minor|patch Semantic version bump type (defaults to minor when omitted) 14 22 15 - if ! gh auth status &> /dev/null; then 16 - log_error "Not authenticated with GitHub CLI. Please run: gh auth login" 23 + Options: 24 + --notes-file FILE Read release notes from FILE instead of prompting 25 + --dry-run Preview actions without pushing 26 + -h, --help Show this help text 27 + USAGE 28 + } 29 + 30 + ensure_command() { 31 + if ! command -v "$1" >/dev/null 2>&1; then 32 + log_error "$1 not found. Please install it first." 33 + fi 34 + } 35 + 36 + # --- Version helpers --- 37 + parse_version() { 38 + echo "$1" | sed -E 's/^([0-9]+)\.([0-9]+)\.([0-9]+)(-.*)?$/\1 \2 \3 \4/' 39 + } 40 + 41 + bump_version() { 42 + local current_version=$1 43 + local bump_type=$2 44 + 45 + if [[ -z "$current_version" ]]; then 46 + case $bump_type in 47 + major) echo "1.0.0" ;; 48 + minor) echo "0.1.0" ;; 49 + patch) echo "0.0.1" ;; 50 + esac 51 + return 52 + fi 53 + 54 + local parsed=($(parse_version "$current_version")) 55 + local major=${parsed[0]} 56 + local minor=${parsed[1]} 57 + local patch=${parsed[2]} 58 + 59 + case $bump_type in 60 + major) echo "$((major + 1)).0.0" ;; 61 + minor) echo "${major}.$((minor + 1)).0" ;; 62 + patch) echo "${major}.${minor}.$((patch + 1))" ;; 63 + *) log_error "Unknown bump type: $bump_type" ;; 64 + esac 65 + } 66 + 67 + latest_version() { 68 + git tag | grep -E '^v?[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$' | sed 's/^v//' | sort -V | tail -1 69 + } 70 + 71 + # --- Parse arguments --- 72 + VERSION="" 73 + BUMP_TYPE="" 74 + NOTES_FILE="" 75 + DRY_RUN=false 76 + 77 + while [[ $# -gt 0 ]]; do 78 + case "$1" in 79 + --notes-file) 80 + shift 81 + [[ $# -gt 0 ]] || log_error "--notes-file requires a path" 82 + NOTES_FILE=$1 83 + ;; 84 + --dry-run) 85 + DRY_RUN=true 86 + ;; 87 + -h|--help) 88 + usage 89 + exit 0 90 + ;; 91 + major|minor|patch) 92 + if [[ -n "$VERSION" ]]; then 93 + log_error "Cannot specify both explicit version and bump type." 94 + fi 95 + BUMP_TYPE=$1 96 + ;; 97 + *) 98 + if [[ -z "$VERSION" ]]; then 99 + VERSION=$1 100 + else 101 + log_error "Unexpected argument: $1" 102 + fi 103 + ;; 104 + esac 105 + shift || true 106 + done 107 + 108 + ensure_command gh 109 + ensure_command jq 110 + 111 + if ! gh auth status >/dev/null 2>&1; then 112 + log_error "GitHub CLI is not authenticated. Run 'gh auth login'." 17 113 fi 18 114 19 - # --- Get version --- 20 - log_info "Fetching latest release information..." 115 + REPO_NAME=$(gh repo view --json nameWithOwner -q .nameWithOwner) 21 116 22 - # Get the latest tag from remote 23 - LATEST_TAG=$(git ls-remote --tags origin | grep -E 'refs/tags/v[0-9]+\.[0-9]+\.[0-9]+' | sed 's/.*refs\/tags\///' | sort -V | tail -1) 117 + git fetch --tags >/dev/null 2>&1 || true 24 118 25 - if [ -n "$LATEST_TAG" ]; then 26 - LATEST_VERSION=${LATEST_TAG#v} # Remove 'v' prefix 27 - log_info "Latest version on origin: $LATEST_VERSION" 119 + LATEST_VERSION=$(latest_version) 120 + if [[ -n "$LATEST_VERSION" ]]; then 121 + log_info "Latest version: $LATEST_VERSION" 28 122 else 29 - log_info "No previous releases found on origin" 123 + log_info "No previous versions found." 124 + fi 125 + 126 + if [[ -n "$VERSION" && -n "$BUMP_TYPE" ]]; then 127 + log_error "Specify either an explicit version or a bump type, not both." 30 128 fi 31 129 32 - echo "" 33 - echo "Enter the version for the new release (e.g., 1.0.0):" 34 - read -p "Version: " VERSION 130 + if [[ -n "$BUMP_TYPE" ]]; then 131 + VERSION=$(bump_version "$LATEST_VERSION" "$BUMP_TYPE") 132 + log_info "Bump type '$BUMP_TYPE' selected -> $VERSION" 133 + fi 35 134 36 - if [ -z "$VERSION" ]; then 37 - log_error "No version entered. Aborting." 135 + if [[ -z "$VERSION" ]]; then 136 + DEFAULT_VERSION=$(bump_version "$LATEST_VERSION" "minor") 137 + read -rp "Enter version [$DEFAULT_VERSION]: " VERSION_INPUT 138 + if [[ -z "$VERSION_INPUT" ]]; then 139 + VERSION=$DEFAULT_VERSION 140 + else 141 + VERSION=$VERSION_INPUT 142 + fi 38 143 fi 39 144 40 - # Validate version format 145 + [[ -n "$VERSION" ]] || log_error "No version provided." 146 + 41 147 if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$ ]]; then 42 - log_error "Invalid version format: '$VERSION'. Must be x.y.z or x.y.z-tag.n (e.g., 1.4.0 or 1.4.0-beta.3)" 148 + log_error "Invalid version format '$VERSION'." 43 149 fi 44 150 45 151 TAG_NAME="v$VERSION" 46 152 47 - # --- Check if tag already exists --- 48 153 if git rev-parse "$TAG_NAME" >/dev/null 2>&1; then 49 - log_error "Tag $TAG_NAME already exists locally. Please choose a different version." 154 + log_error "Tag $TAG_NAME already exists locally." 50 155 fi 51 156 52 157 if git ls-remote --tags origin | grep -q "refs/tags/$TAG_NAME$"; then 53 - log_error "Tag $TAG_NAME already exists on remote. Please choose a different version." 158 + log_error "Tag $TAG_NAME already exists on origin." 54 159 fi 55 160 56 - # --- Get release notes --- 57 - echo "" 58 - echo "Enter release notes (press Ctrl+D when finished):" 59 - RELEASE_NOTES=$(cat) 161 + if ! git diff-index --quiet HEAD --; then 162 + log_error "Working tree is not clean. Commit or stash changes first." 163 + fi 164 + 165 + if [[ -n "$NOTES_FILE" ]]; then 166 + [[ -f "$NOTES_FILE" ]] || log_error "Release notes file not found: $NOTES_FILE" 167 + RELEASE_NOTES=$(<"$NOTES_FILE") 168 + else 169 + log_info "Enter release notes (Ctrl+D to finish):" 170 + RELEASE_NOTES=$(cat) 171 + fi 172 + 173 + if [[ -z "$RELEASE_NOTES" ]]; then 174 + RELEASE_NOTES="Release $TAG_NAME" 175 + fi 176 + 177 + log_info "Preparing release for $TAG_NAME" 178 + log_info "Workflow: $WORKFLOW_NAME" 60 179 61 - if [ -z "$RELEASE_NOTES" ]; then 62 - RELEASE_NOTES="Release $TAG_NAME" 180 + TMP_NOTES=$(mktemp) 181 + printf '%s\n' "$RELEASE_NOTES" > "$TMP_NOTES" 182 + trap 'rm -f "$TMP_NOTES"' EXIT 183 + 184 + if $DRY_RUN; then 185 + log_info "[dry-run] Would create annotated tag $TAG_NAME" 186 + else 187 + git tag -a "$TAG_NAME" -F "$TMP_NOTES" 188 + log_success "Tag $TAG_NAME created." 63 189 fi 64 190 65 - # --- Determine if prerelease --- 66 - PRERELEASE_FLAG="" 67 - if [[ "$VERSION" == *"-"* ]]; then 68 - PRERELEASE_FLAG="--prerelease" 69 - log_info "This will be marked as a pre-release." 191 + CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) 192 + HEAD_SHA=$(git rev-parse HEAD) 193 + 194 + if $DRY_RUN; then 195 + log_info "[dry-run] Would push branch $CURRENT_BRANCH and tag $TAG_NAME" 196 + else 197 + git push origin "$CURRENT_BRANCH" 198 + git push origin "$TAG_NAME" 199 + log_success "Tag $TAG_NAME pushed to origin." 70 200 fi 71 201 72 - # --- Create release --- 73 - log_info "Creating GitHub release $TAG_NAME..." 202 + if $DRY_RUN; then 203 + log_info "[dry-run] Skipping workflow monitoring." 204 + exit 0 205 + fi 74 206 75 - gh release create "$TAG_NAME" \ 76 - --title "Release $TAG_NAME" \ 77 - --notes "$RELEASE_NOTES" \ 78 - --prerelease 207 + log_info "Waiting for GitHub Actions workflow to start..." 208 + RUN_ID="" 209 + FALLBACK_USED=false 210 + for attempt in {1..30}; do 211 + RUN_JSON=$(gh run list --workflow "$WORKFLOW_IDENTIFIER" --limit 10 --json databaseId,headSha,status,event 2>/dev/null || true) 212 + if [[ -z "$RUN_JSON" ]]; then 213 + RUN_JSON="[]" 214 + fi 215 + RUN_ID=$(echo "$RUN_JSON" | jq -r ".[] | select(.headSha == \"$HEAD_SHA\") | .databaseId" | head -n1) 216 + if [[ -z "$RUN_ID" && "$WORKFLOW_IDENTIFIER" == "$WORKFLOW_FILE" && "$FALLBACK_USED" == "false" ]]; then 217 + WORKFLOW_IDENTIFIER="$WORKFLOW_NAME" 218 + FALLBACK_USED=true 219 + continue 220 + fi 221 + if [[ -n "$RUN_ID" ]]; then 222 + break 223 + fi 224 + sleep 10 225 + done 79 226 80 - log_success "Pre-release $TAG_NAME created successfully!" 81 - log_info "The GitHub Actions workflow is now building and will attach the artifacts automatically." 82 - log_info "Once the build completes, you can promote it to a full release at:" 83 - log_info "https://github.com/$(gh repo view --json owner,name -q '.owner.login + "/" + .name')/releases" 227 + if [[ -z "$RUN_ID" ]]; then 228 + log_error "Could not find workflow run for commit $HEAD_SHA. Monitor manually on GitHub." 229 + fi 84 230 85 - echo "" 86 - echo "To monitor the build progress:" 87 - echo "gh run list --workflow=build-and-release.yml" 231 + log_info "Monitoring workflow run ID $RUN_ID..." 232 + if gh run watch "$RUN_ID" --exit-status; then 233 + log_success "Workflow succeeded!" 234 + log_info "Release will be published automatically." 235 + log_info "https://github.com/$REPO_NAME/releases/tag/$TAG_NAME" 236 + else 237 + log_info "Workflow failed. Cleaning up..." 238 + if gh release view "$TAG_NAME" >/dev/null 2>&1; then 239 + gh release delete "$TAG_NAME" --yes || true 240 + fi 241 + git push origin ":refs/tags/$TAG_NAME" || true 242 + git tag -d "$TAG_NAME" || true 243 + log_error "Release workflow failed. Fix issues and rerun the script with version $VERSION." 244 + fi