Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
13c70ba
fix naming error
frequency403 Mar 24, 2026
01162ec
fix icon name
frequency403 Mar 24, 2026
fe3ef20
Fix startup error when files are not readable. (#18)
frequency403 Mar 24, 2026
b3e8a9c
remove warnings as error in csproj
frequency403 Mar 24, 2026
bbbe8ea
remove warnings as error in csproj
frequency403 Mar 24, 2026
f536d95
Merge branch 'master' into development
frequency403 Mar 24, 2026
e4807aa
Merge branch 'development' into release/v3.0.1
frequency403 Mar 24, 2026
d99e92f
Fix and Optimize Configuration, SSH Key Handling, and UI (#21)
frequency403 Jun 9, 2026
8ee5e98
Introduce new workflows: auto-tag refinement, unit tests, build-and-p…
frequency403 Jun 9, 2026
5fbbd39
Bump version to 3.1.0, improve PKGBUILD formatting, and fix SSH key f…
frequency403 Jun 9, 2026
cdff101
Add `build-and-package.yml` and `test.yml` workflows; refine asset pr…
frequency403 Jun 9, 2026
1431a2d
Refactor workflows: streamline asset preparation, reorganize director…
frequency403 Jun 9, 2026
638c3b8
Update `staging.yml`: adjust dependencies for `deploy-aur-nightly` an…
frequency403 Jun 9, 2026
d7f6b4b
Refactor GitHub Actions workflows: improve artifact handling and enha…
frequency403 Jun 9, 2026
ec44535
Update `staging.yml`: add `build` dependency for `deploy-aur-nightly`…
frequency403 Jun 9, 2026
08afa89
Revamp README: overhaul content structure, include new features, upda…
frequency403 Jun 9, 2026
f9f6e48
Bump version to 3.1.1; update PKGBUILD, normalize version logic in wo…
frequency403 Jun 9, 2026
107d24e
Merge branch 'master' into release/v3.1.1
frequency403 Jun 9, 2026
4a91f0a
Bump version to 3.1.2; update PKGBUILD, enhance version normalization…
frequency403 Jun 10, 2026
2ba45d1
Merge branch 'master' into release/v3.1.2
frequency403 Jun 10, 2026
75c82ec
Enhance Komac installation in workflow: dynamically fetch latest rele…
frequency403 Jun 10, 2026
2ae51bf
Refactor GitHub Actions: modularize workflows, add custom composite a…
frequency403 Jun 10, 2026
36859b5
Merge remote-tracking branch 'origin/master' into release/v3.1.3
frequency403 Jun 10, 2026
e4f25b4
Add `actions/checkout@v4` to workflows for consistent repository access.
frequency403 Jun 10, 2026
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
127 changes: 127 additions & 0 deletions .github/actions/build-appimage/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
name: 'Build AppImage'
description: >
Packages a self-contained .NET binary into an AppImage for linux-x64.
Requires the repository to be checked out and the binary to exist at the given path.
Note: FUSE is not available on GitHub-hosted runners, so appimagetool runs via
--appimage-extract-and-run.

inputs:
binary-path:
description: 'Path to the compiled binary to package'
required: true
version:
description: 'Application version (no v-prefix; may contain + for nightly builds)'
required: true
is-nightly:
description: 'Set to "true" to replace + with ~ in the AppStream version'
required: false
default: 'false'
asset-prefix:
description: 'Filename prefix for the output AppImage'
required: false
default: 'OpenSSH-GUI'
appimagetool-tag:
description: 'Release tag of appimagetool to download'
required: false
default: 'continuous'

outputs:
appimage-name:
description: 'Filename of the produced AppImage'
value: ${{ steps.build.outputs.appimage-name }}

runs:
using: composite
steps:
- name: Install AppImage build dependencies
shell: bash
run: |
set -euo pipefail
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends librsvg2-bin appstream

- name: Download appimagetool
shell: bash
run: |
set -euo pipefail
TOOL_URL="https://github.com/AppImage/appimagetool/releases/download/${{ inputs.appimagetool-tag }}/appimagetool-x86_64.AppImage"
echo "::notice::Downloading appimagetool from $TOOL_URL"
wget --progress=dot:giga "$TOOL_URL" -O appimagetool
chmod +x appimagetool

- name: Assemble and build AppImage
id: build
shell: bash
run: |
set -euo pipefail

BINARY_PATH="${{ inputs.binary-path }}"
VERSION="${{ inputs.version }}"
IS_NIGHTLY="${{ inputs.is-nightly }}"
ASSET_PREFIX="${{ inputs.asset-prefix }}"
APP_ID="io.github.frequency403.openssh_gui"

# Validate all required source files exist before doing any work
for REQUIRED in \
"$BINARY_PATH" \
"images/openssh-gui.svg" \
"appimage/${APP_ID}.metainfo.xml" \
"appimage/AppRun"
do
if [[ ! -f "$REQUIRED" ]]; then
echo "::error::Required file not found: $REQUIRED"
exit 1
fi
done

# Prepare AppDir layout
mkdir -p "AppDir/usr/bin"
mkdir -p "AppDir/usr/share/icons/hicolor/256x256/apps"
mkdir -p "AppDir/usr/share/icons/hicolor/scalable/apps"
mkdir -p "AppDir/usr/share/applications"
mkdir -p "AppDir/usr/share/metainfo"

cp "$BINARY_PATH" AppDir/usr/bin/openssh-gui
chmod +x AppDir/usr/bin/openssh-gui

# Icons
rsvg-convert -w 256 -h 256 images/openssh-gui.svg \
-o AppDir/usr/share/icons/hicolor/256x256/apps/openssh-gui.png
cp images/openssh-gui.svg \
AppDir/usr/share/icons/hicolor/scalable/apps/openssh-gui.svg
cp AppDir/usr/share/icons/hicolor/256x256/apps/openssh-gui.png AppDir/openssh-gui.png
cp AppDir/usr/share/icons/hicolor/256x256/apps/openssh-gui.png AppDir/appicon.png

# Metainfo: patch placeholder version and date
METAINFO_DEST="AppDir/usr/share/metainfo/${APP_ID}.metainfo.xml"
cp "appimage/${APP_ID}.metainfo.xml" "$METAINFO_DEST"

APPSTREAM_VERSION="$VERSION"
if [[ "$IS_NIGHTLY" == "true" ]]; then
APPSTREAM_VERSION="${APPSTREAM_VERSION//+/~}"
fi
sed -i \
"s|<release version=\"0.0.0\" date=\"1970-01-01\"/>|<release version=\"${APPSTREAM_VERSION}\" date=\"$(date +%Y-%m-%d)\"/>|" \
"$METAINFO_DEST"

# Generate .desktop file from metainfo
appstreamcli make-desktop-file \
"$METAINFO_DEST" \
"AppDir/usr/share/applications/${APP_ID}.desktop"
cp "AppDir/usr/share/applications/${APP_ID}.desktop" \
"AppDir/${APP_ID}.desktop"

cp appimage/AppRun AppDir/AppRun
chmod +x AppDir/AppRun

# Assemble AppImage (--appimage-extract-and-run bypasses FUSE requirement)
APPIMAGE_NAME="${ASSET_PREFIX}-x86_64.AppImage"
ARCH=x86_64 ./appimagetool --appimage-extract-and-run AppDir "$APPIMAGE_NAME"

if [[ ! -f "$APPIMAGE_NAME" ]]; then
echo "::error::appimagetool did not produce $APPIMAGE_NAME"
exit 1
fi

echo "appimage-name=$APPIMAGE_NAME" >> "$GITHUB_OUTPUT"
echo "::notice::AppImage built successfully: $APPIMAGE_NAME"
43 changes: 43 additions & 0 deletions .github/actions/determine-version/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Determine Version
description: Extract and validate a semantic version from Directory.Build.props and optionally validate against a branch or tag name

outputs:
version:
description: Extracted semantic version without leading v
value: ${{ steps.extract.outputs.version }}
tag:
description: Tag name with leading v
value: ${{ steps.extract.outputs.tag }}

runs:
using: composite

steps:
- name: Extract BaseVersion
id: extract
shell: bash
run: |
set -euo pipefail

FILE="Directory.Build.props"

if [[ ! -f "$FILE" ]]; then
echo "::error::$FILE not found"
exit 1
fi

VERSION="$(dotnet msbuild "$FILE" -nologo -getProperty:BaseVersion | tr -d '\r')"

if [[ -z "$VERSION" ]]; then
echo "::error::BaseVersion not found in $FILE"
exit 1
fi

if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "::error::Invalid BaseVersion: $VERSION"
exit 1
fi

echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "tag=v$VERSION" >> "$GITHUB_OUTPUT"
echo "::notice::Detected version: $VERSION (tag: v$VERSION)"
40 changes: 40 additions & 0 deletions .github/actions/dotnet-publish/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: 'Publish .NET Application'
description: >
Runs dotnet publish for a single-file, ReadyToRun, self-contained Release build.
Assumes the .NET SDK is already set up in the environment.

inputs:
project-path:
description: 'Relative path to the .csproj file to publish'
required: true
runtime:
description: 'Target RID (e.g. win-x64, linux-x64, osx-x64)'
required: true
version:
description: 'Version string passed to MSBuild /p:Version (no v-prefix)'
required: true
output-dir:
description: 'Output directory relative to the workspace root'
required: false
default: './publish'
extra-msbuild-args:
description: 'Additional MSBuild property flags, e.g. "-p:IsNightly=true"'
required: false
default: ''

runs:
using: composite
steps:
- name: Publish application
shell: bash
run: |
set -euo pipefail
dotnet publish "${{ inputs.project-path }}" \
--configuration Release \
--runtime "${{ inputs.runtime }}" \
--output "${{ inputs.output-dir }}" \
-p:PublishSingleFile=true \
-p:PublishReadyToRun=true \
-p:IncludeNativeLibrariesForSelfExtract=true \
-p:Version="${{ inputs.version }}" \
${{ inputs.extra-msbuild-args }}
59 changes: 59 additions & 0 deletions .github/actions/dotnet-setup/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: 'Setup .NET Environment'
description: >
Checks out the repository, auto-detects the TargetFramework from
Directory.Build.props, sets up the .NET SDK, and restores the NuGet cache.

inputs:
fetch-depth:
description: 'Number of commits to fetch (0 = full history, 1 = shallow)'
required: false
default: '1'
token:
description: 'GitHub token used for checkout'
required: false
default: ${{ github.token }}

outputs:
dotnet-version:
description: 'Resolved .NET version string, e.g. "10.0.x"'
value: ${{ steps.detect-tfm.outputs.version }}

runs:
using: composite
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: ${{ inputs.fetch-depth }}
token: ${{ inputs.token }}

- name: Detect TargetFramework from Directory.Build.props
id: detect-tfm
shell: bash
run: |
set -euo pipefail
PROPS_FILE="Directory.Build.props"
if [[ ! -f "$PROPS_FILE" ]]; then
echo "::error::$PROPS_FILE not found in workspace root"
exit 1
fi
TFM=$(grep -oPm1 '(?<=<TargetFramework>net)[0-9.]+' "$PROPS_FILE" || true)
if [[ -z "$TFM" ]]; then
echo "::error::Could not extract <TargetFramework> from $PROPS_FILE"
exit 1
fi
echo "version=${TFM}.x" >> "$GITHUB_OUTPUT"
echo "::notice::Resolved .NET version: ${TFM}.x"

- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ steps.detect-tfm.outputs.version }}

- name: Restore NuGet cache
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/Directory.Packages.props', '**/Directory.Build.props') }}
restore-keys: |
${{ runner.os }}-nuget-
37 changes: 37 additions & 0 deletions .github/actions/install-komac/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: 'Install Komac'
description: >
Resolves and downloads the latest Komac (linux-amd64) release from GitHub
and makes it executable in the current working directory.

outputs:
komac-path:
description: 'Absolute path to the komac binary'
value: ${{ steps.install.outputs.komac-path }}

runs:
using: composite
steps:
- name: Resolve and download Komac
id: install
shell: bash
run: |
set -euo pipefail

KOMAC_URL=$(curl -fsSL \
https://api.github.com/repos/russellbanks/Komac/releases/latest \
| grep -o '"browser_download_url": "[^"]*linux-amd64[^"]*"' \
| grep -v '\.sha' \
| head -1 \
| cut -d'"' -f4)

if [[ -z "$KOMAC_URL" ]]; then
echo "::error::Failed to resolve Komac download URL from GitHub Releases API"
exit 1
fi

echo "::notice::Downloading Komac from: $KOMAC_URL"
curl -fsSL "$KOMAC_URL" -o komac
chmod +x komac
./komac --version

echo "komac-path=$(pwd)/komac" >> "$GITHUB_OUTPUT"
31 changes: 15 additions & 16 deletions .github/workflows/auto-tag.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,17 @@ on:
permissions:
contents: write

concurrency:
group: auto-tag-${{ github.ref }}
cancel-in-progress: false

jobs:
create-tag:
name: Create and Push Tag
# Only run when a release/* branch was actually merged (not just closed)
if: github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'release/')
runs-on: ubuntu-latest
timeout-minutes: 5

steps:
- name: Checkout repository
Expand All @@ -24,34 +29,28 @@ jobs:
fetch-depth: 0
token: ${{ secrets.PAT_TOKEN }}

- name: Extract version from branch name
- name: Extract and validate version from branch name
id: version
run: |
BRANCH="${{ github.event.pull_request.head.ref }}"
# Strip 'release/' prefix
VERSION="${BRANCH#release/}"
# Strip optional 'v' prefix
VERSION="${VERSION#v}"

echo "VERSION=$VERSION" >> "$GITHUB_OUTPUT"
echo "TAG=v${VERSION}" >> "$GITHUB_OUTPUT"
echo "::notice ::Detected version: $VERSION (tag: v${VERSION})"
uses: ./.github/actions/determine-version

- name: Check if tag already exists
id: check
run: |
set -euo pipefail
if git rev-parse "refs/tags/${{ steps.version.outputs.TAG }}" >/dev/null 2>&1; then
echo "exists=true" >> "$GITHUB_OUTPUT"
echo "::warning ::Tag ${{ steps.version.outputs.TAG }} already exists, skipping."
echo "exists=true" >> "$GITHUB_OUTPUT"
echo "::warning::Tag ${{ steps.version.outputs.TAG }} already exists, skipping."
else
echo "exists=false" >> "$GITHUB_OUTPUT"
fi

- name: Create and push tag
if: steps.check.outputs.exists == 'false'
run: |
git config user.name "github-actions[bot]"
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "${{ steps.version.outputs.TAG }}" -m "Release ${{ steps.version.outputs.VERSION }}"
git tag -a "${{ steps.version.outputs.TAG }}" \
-m "Release ${{ steps.version.outputs.VERSION }}"
git push origin "${{ steps.version.outputs.TAG }}"
echo "::notice ::Tag ${{ steps.version.outputs.TAG }} pushed successfully."
echo "::notice::Tag ${{ steps.version.outputs.TAG }} pushed successfully."
Loading
Loading