From c0b0ae3b74c810b981bfcf4778e3be1207edfdb0 Mon Sep 17 00:00:00 2001 From: Rudi Date: Thu, 20 Aug 2026 19:27:55 -0400 Subject: [PATCH] Add releases --- .gitea/issue_template/bug_report.yaml | 64 ++++++++++++ .gitea/issue_template/feature_request.yaml | 32 ++++++ .gitea/workflows/build.yaml | 61 +++++++++++ .gitea/workflows/build.yml | 51 ---------- .gitea/workflows/release.yaml | 88 ++++++++++++++++ README.md | 111 +++++++++++++++------ 6 files changed, 327 insertions(+), 80 deletions(-) create mode 100644 .gitea/issue_template/bug_report.yaml create mode 100644 .gitea/issue_template/feature_request.yaml create mode 100644 .gitea/workflows/build.yaml delete mode 100644 .gitea/workflows/build.yml create mode 100644 .gitea/workflows/release.yaml diff --git a/.gitea/issue_template/bug_report.yaml b/.gitea/issue_template/bug_report.yaml new file mode 100644 index 0000000..6c76054 --- /dev/null +++ b/.gitea/issue_template/bug_report.yaml @@ -0,0 +1,64 @@ +name: Bug Report +about: Create a report to help us fix an issue with a3270 +title: "[BUG] " +labels: + - bug +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to report an issue! + - type: input + id: host_environment + attributes: + label: Host Environment + description: What mainframe / operating system was connected (e.g., z/OS, z/VM, CMS, TSO, MVS 3.8j, Hercules/TK4-)? + placeholder: "e.g. z/VM 7.2 CMS, Hercules TK4-" + validations: + required: false + - type: input + id: android_device + attributes: + label: Android Device & OS Version + description: What Android device and Android version are you using? + placeholder: "e.g. Pixel 8, Android 14 (API 34)" + validations: + required: false + - type: dropdown + id: terminal_model + attributes: + label: Terminal Model + description: Terminal model in use + options: + - 3279-2 (Color 24x80) + - 3279-3 (Color 32x80) + - 3279-4 (Color 43x80) + - 3279-5 (Color 27x132) + - 3278 (Monochrome) + validations: + required: true + - type: textarea + id: description + attributes: + label: Problem Description + description: A clear and concise description of what went wrong. + validations: + required: true + - type: textarea + id: steps + attributes: + label: Steps To Reproduce + description: Steps to reproduce the behavior. + placeholder: | + 1. Connect to host '...' + 2. Open File Transfer dialog + 3. Start IND$FILE GET ... + 4. See error ... + validations: + required: true + - type: textarea + id: logs + attributes: + label: Logs / Logcat Output + description: Paste any logcat or debug logs here. + render: shell diff --git a/.gitea/issue_template/feature_request.yaml b/.gitea/issue_template/feature_request.yaml new file mode 100644 index 0000000..76a44b8 --- /dev/null +++ b/.gitea/issue_template/feature_request.yaml @@ -0,0 +1,32 @@ +name: Feature Request +about: Suggest an idea or enhancement for a3270 +title: "[FEATURE] " +labels: + - enhancement +body: + - type: markdown + attributes: + value: | + Thanks for suggesting a feature or enhancement for a3270! + - type: textarea + id: problem + attributes: + label: Is your feature request related to a problem? + description: A clear and concise description of what the problem or missing capability is. + placeholder: "I'm always frustrated when [...]" + validations: + required: false + - type: textarea + id: solution + attributes: + label: Describe the solution you'd like + description: A clear and concise description of what you want to happen. + validations: + required: true + - type: textarea + id: alternatives + attributes: + label: Describe alternatives you've considered + description: Any alternative solutions or features you've considered. + validations: + required: false diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml new file mode 100644 index 0000000..8f40f8d --- /dev/null +++ b/.gitea/workflows/build.yaml @@ -0,0 +1,61 @@ +name: Build and Test a3270 + +on: + push: + branches: [ "main", "master", "develop" ] + pull_request: + branches: [ "main", "master" ] + workflow_dispatch: + +jobs: + build: + name: Build Android APK + runs-on: ubuntu-latest + + steps: + - name: Configure Dynamic DNS Resolvers + run: | + printf "nameserver 1.1.1.1\nnameserver 8.8.8.8\n" > /etc/resolv.conf 2>/dev/null || true + echo "64.22.43.251 git.hugfreevikings.wtf" | sudo tee -a /etc/hosts || echo "64.22.43.251 git.hugfreevikings.wtf" >> /etc/hosts || true + + - name: Checkout Code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Checkout j3270 (for lib3270j) + run: | + git clone --depth 1 https://git.hugfreevikings.wtf/rudi/j3270.git j3270 + + - name: Set up Java JDK (Temurin 21) + uses: actions/setup-java@v3 + continue-on-error: true + with: + java-version: '21' + distribution: 'temurin' + + - name: Set up Android SDK + uses: android-actions/setup-android@v3 + + - name: Make scripts executable + run: | + chmod +x ./gradlew ./build.sh + + - name: Build Android APK + run: | + ./gradlew assembleDebug --no-daemon + env: + J3270_DIR: ${{ github.workspace }}/j3270 + + - name: Upload a3270 APK Artifact + uses: actions/upload-artifact@v3 + continue-on-error: true + with: + name: a3270-debug + path: build/outputs/apk/debug/a3270-debug.apk + + - name: Report Build Status + if: failure() + run: | + echo "### ❌ Build Failed" >> $GITHUB_STEP_SUMMARY + echo "Please check the runner logs above for details." >> $GITHUB_STEP_SUMMARY diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml deleted file mode 100644 index 1f53b07..0000000 --- a/.gitea/workflows/build.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: Build a3270 APK - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - workflow_dispatch: - -jobs: - build: - name: Build Android APK - runs-on: ubuntu-latest - - steps: - - name: Configure DNS and Hosts - run: | - echo "64.22.43.251 git.hugfreevikings.wtf" | sudo tee -a /etc/hosts || echo "64.22.43.251 git.hugfreevikings.wtf" >> /etc/hosts || true - echo "nameserver 1.1.1.1" | sudo tee -a /etc/resolv.conf || echo "nameserver 1.1.1.1" >> /etc/resolv.conf || true - echo "nameserver 8.8.8.8" | sudo tee -a /etc/resolv.conf || echo "nameserver 8.8.8.8" >> /etc/resolv.conf || true - - - name: Checkout a3270 - uses: actions/checkout@v4 - - - name: Checkout j3270 (for lib3270j) - run: | - git clone --depth 1 https://git.hugfreevikings.wtf/rudi/j3270.git j3270 - - - name: Set up JDK 21 - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: '21' - - - name: Set up Android SDK - uses: android-actions/setup-android@v3 - - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - - name: Build Debug APK - run: ./gradlew assembleDebug --no-daemon - env: - J3270_DIR: ${{ github.workspace }}/j3270 - - - name: Upload APK Artifact - uses: actions/upload-artifact@v3 - with: - name: a3270-debug - path: build/outputs/apk/debug/a3270-debug.apk - if-no-files-found: error diff --git a/.gitea/workflows/release.yaml b/.gitea/workflows/release.yaml new file mode 100644 index 0000000..ee1b03d --- /dev/null +++ b/.gitea/workflows/release.yaml @@ -0,0 +1,88 @@ +name: Release a3270 + +on: + push: + tags: + - 'v*' + +jobs: + release: + name: Build & Publish Release + runs-on: ubuntu-latest + + steps: + - name: Configure Dynamic DNS Resolvers + run: | + printf "nameserver 1.1.1.1\nnameserver 8.8.8.8\n" > /etc/resolv.conf 2>/dev/null || true + echo "64.22.43.251 git.hugfreevikings.wtf" | sudo tee -a /etc/hosts || echo "64.22.43.251 git.hugfreevikings.wtf" >> /etc/hosts || true + + - name: Checkout Code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Checkout j3270 (for lib3270j) + run: | + git clone --depth 1 https://git.hugfreevikings.wtf/rudi/j3270.git j3270 + + - name: Set up Java JDK (Temurin 21) + uses: actions/setup-java@v3 + continue-on-error: true + with: + java-version: '21' + distribution: 'temurin' + + - name: Set up Android SDK + uses: android-actions/setup-android@v3 + + - name: Make scripts executable + run: | + chmod +x ./gradlew ./build.sh + + - name: Build Android APK + run: | + ./gradlew assembleDebug --no-daemon + env: + J3270_DIR: ${{ github.workspace }}/j3270 + + - name: Upload Release Artifact + uses: actions/upload-artifact@v3 + continue-on-error: true + with: + name: a3270-${{ github.ref_name }} + path: build/outputs/apk/debug/a3270-debug.apk + + - name: Publish Release to Gitea + continue-on-error: true + run: | + TAG="${{ github.ref_name }}" + API_URL="${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases" + TOKEN="${{ secrets.GITHUB_TOKEN }}" + APK_FILE="build/outputs/apk/debug/a3270-debug.apk" + + IS_PRERELEASE="false" + case "$TAG" in + *beta*|*alpha*|*rc*|*pre*|*preview*|*dev*) + IS_PRERELEASE="true" + echo "Tag $TAG detected as pre-release / beta." + ;; + esac + + echo "Creating Release for tag $TAG (prerelease=$IS_PRERELEASE)..." + RESPONSE=$(curl -s -X POST "$API_URL" \ + -H "Authorization: token $TOKEN" \ + -H "Content-Type: application/json" \ + -d "{\"tag_name\": \"$TAG\", \"name\": \"a3270 $TAG\", \"body\": \"Automated release for **$TAG**.\n\n### Installing a3270\n```bash\nadb install -r a3270.apk\n```\", \"draft\": false, \"prerelease\": $IS_PRERELEASE}") + + RELEASE_ID=$(echo "$RESPONSE" | grep -o '"id":[0-9]*' | head -n 1 | cut -d':' -f2) + + if [ -n "$RELEASE_ID" ] && [ -f "$APK_FILE" ]; then + echo "Uploading $APK_FILE to release $RELEASE_ID as a3270.apk..." + curl -s -X POST "$API_URL/$RELEASE_ID/assets?name=a3270.apk" \ + -H "Authorization: token $TOKEN" \ + -H "Content-Type: application/vnd.android.package-archive" \ + --data-binary @"$APK_FILE" + echo "Release $TAG published with a3270.apk attached successfully!" + else + echo "Release API response: $RESPONSE" + fi diff --git a/README.md b/README.md index b88efd6..fe4f0e5 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,64 @@ # a3270 -An Android TN3270 terminal emulator based on `lib3270j` / `j3270`, built with Jetpack Compose and ported using AI. +[![Build & Test](https://git.hugfreevikings.wtf/rudi/a3270/actions/workflows/build.yaml/badge.svg)](https://git.hugfreevikings.wtf/rudi/a3270/actions) +[![Latest Release](https://git.hugfreevikings.wtf/rudi/a3270/badges/release.svg)](https://git.hugfreevikings.wtf/rudi/a3270/releases) +[![Android](https://img.shields.io/badge/Android-API%2024%2B%20%7C%20Compose-green.svg)](https://developer.android.com) +[![License](https://img.shields.io/badge/License-MIT%20%2F%20BSD-green.svg)](LICENSE) -## Repositories +An **x3270-aligned IBM 3270 mainframe terminal emulator for Android**, written in Kotlin using **Jetpack Compose** and powered by the `lib3270j` protocol engine from [j3270](https://git.hugfreevikings.wtf/rudi/j3270). Designed for mobile access to IBM z/OS, z/VM, CMS, and TSO systems over TN3270 and TN3270E. + +--- + +## 🔗 Repositories - **a3270 (Android App)**: https://git.hugfreevikings.wtf/rudi/a3270 - **j3270 (Desktop App & `lib3270j` Core Library)**: https://git.hugfreevikings.wtf/rudi/j3270 `a3270` relies on the shared 3270 protocol library (`lib3270j`) provided by the `j3270` repository. -## CI/CD & Automated Builds +--- -Automated CI/CD is configured using Gitea Actions (`.gitea/workflows/build.yml`). On every push to `main` or pull request, the workflow automatically: -1. Clones `a3270` and `j3270` (`lib3270j`). -2. Configures JDK 21 and the Android SDK. -3. Builds the debug APK (`a3270-debug.apk`). -4. Uploads the resulting APK as a downloadable workflow artifact (`a3270-debug`). +## 🚀 Quick Download & Install -You can download pre-built APKs directly from the **Actions** tab of the repository on Gitea. +### Latest Standalone APK +You can download ready-to-install Android APKs directly from releases or CI builds: -## Prerequisites +- **[Download Latest Tagged Release (`a3270.apk`)](https://git.hugfreevikings.wtf/rudi/a3270/releases)** +- **[Latest Nightly CI Artifacts](https://git.hugfreevikings.wtf/rudi/a3270/actions)** (Download under the latest successful workflow run) +### Installing `a3270` +To install the APK on a connected Android device or emulator via `adb`: + +```bash +adb install -r a3270.apk +``` + +--- + +## ✨ Features + +- **Modern Jetpack Compose UI**: Responsive, touch-optimized 3270 terminal screen rendering. +- **TN3270 & TN3270E Protocol Support**: RFC 2355 compliant state machine, negotiation, Device-Type query, and SSL/TLS encryption via `lib3270j`. +- **On-Screen Function Key Bar**: Quick-access touch bar for PF1–PF24, PA1–PA3, Enter, Clear, Reset, and Attn keys. +- **IND$FILE File Transfer**: Full mobile UI for **CUT** and **DFT (DDM)** high-speed structured field file transfers with ASCII/binary translation, CRLF handling, and recfm/lrecl formatting for TSO, VM/CMS, and CICS. +- **OIA Status Line**: Real-time Operator Information Area displaying connection state, keyboard lock, insert mode, cursor row/col, and LU name. +- **Host Profile Storage**: Save and manage multiple mainframe connection profiles with custom ports, models, and SSL/TLS options. +- **Background Session Persistence**: Android foreground service keeps your terminal session alive when multitasking. +- **Terminal Models**: + - Model 2 (24x80) + - Model 3 (32x80) + - Model 4 (43x80) + - Model 5 (27x132) + +--- + +## 🛠️ Building from Source + +### Prerequisites - **Java Development Kit (JDK)**: JDK 17 or JDK 21 -- **Android SDK**: Android SDK with API 34 platforms and build-tools installed (`ANDROID_HOME` or `ANDROID_SDK_ROOT` set) - -## Setup & Building +- **Android SDK**: API 34 platforms and build-tools (`ANDROID_HOME` or `ANDROID_SDK_ROOT` set) ### 1. Clone Repositories Side-by-Side - For the easiest setup, clone both `j3270` and `a3270` into the same parent folder: ```bash @@ -46,15 +77,12 @@ cd a3270 > ``` ### 2. Build the Debug APK - -You can build using the helper script: - +Using the helper script: ```bash ./build.sh ``` -Or directly with Gradle Wrapper: - +Or using Gradle Wrapper directly: ```bash ./gradlew assembleDebug ``` @@ -64,25 +92,50 @@ The compiled APK will be generated at: build/outputs/apk/debug/a3270-debug.apk ``` -## Deployment / Installation +--- -To install the debug APK onto a connected Android device or emulator via `adb`: +## 🏷️ How to Tag & Publish (Beta) Releases + +Releases can be published automatically to Gitea via Git tags: ```bash -adb install -r build/outputs/apk/debug/a3270-debug.apk +# 1. Create a version tag (stable or beta) +git tag -a v0.1.0-beta.1 -m "Release v0.1.0-beta.1" + +# Or a stable release: +git tag -a v0.1.0 -m "Release v0.1.0" + +# 2. Push the tag to Gitea +git push origin v0.1.0-beta.1 ``` -## Contributing +Pushing any `v*` tag triggers the automated release workflow (`.gitea/workflows/release.yaml`), which: +1. Automatically detects pre-releases (tags containing `beta`, `alpha`, `rc`, `pre`, `preview`, or `dev`) and marks them as **Pre-release** on Gitea. +2. Builds the APK and packages it. +3. Publishes a new Gitea Release with `a3270.apk` attached automatically. -Contributions are welcome! +--- + +## 🔍 Topics & Discoverability + +Suggested keywords/topics for repository indexing: +`tn3270` · `tn3270e` · `ibm-3270` · `mainframe` · `terminal-emulator` · `android` · `android-app` · `jetpack-compose` · `kotlin` · `x3270` · `j3270` · `ind-file` · `file-transfer` · `ebcdic` · `z-vm` · `z-os` · `cms` · `tso` · `cics` + +--- + +## 🤝 Contributing + +Contributions are welcome! Please feel free to open a bug report or feature request via the Gitea issue tracker. I have contributed no code to this project, it was entirely written by LLMs with my guidance only. -## Acknowledgements +--- - - [j3270](https://git.hugfreevikings.wtf/rudi/j3270) - - [x3270](https://x3270.miraheze.org/wiki/Main_Page) +## 📜 Acknowledgements + + - [j3270](https://git.hugfreevikings.wtf/rudi/j3270) — Desktop emulator and `lib3270j` protocol engine + - [x3270](https://x3270.miraheze.org/wiki/Main_Page) — Reference C implementation and protocol specifications - [Antigravity](https://antigravity.google) - Claude Opus 4.6 - - Gemini 3.1 Pro (High) - - Gemini 3.7 Flash (High) + - Gemini 3.1 Pro + - Gemini 3.7 Flash