Skip to content

Commit 9049c30

Browse files
committed
initial release: sf — terminal file manager written in ARO
Midnight Commander-style dual-panel file manager with keyboard navigation, file preview, and directory browsing. Includes CI/CD pipeline with signed macOS builds and Homebrew tap support.
0 parents  commit 9049c30

File tree

9 files changed

+851
-0
lines changed

9 files changed

+851
-0
lines changed

.github/workflows/build.yml

Lines changed: 322 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,322 @@
1+
name: Build & Release
2+
3+
on:
4+
push:
5+
branches: [main]
6+
tags: ['*']
7+
pull_request:
8+
branches: [main]
9+
10+
env:
11+
ARO_VERSION: latest
12+
13+
jobs:
14+
# ===========================================================================
15+
# Check — syntax-check on both platforms
16+
# ===========================================================================
17+
check:
18+
strategy:
19+
matrix:
20+
include:
21+
- os: ubuntu-latest
22+
asset: aro-linux-amd64.tar.gz
23+
- os: macos-latest
24+
asset: aro-macos-arm64.tar.gz
25+
runs-on: ${{ matrix.os }}
26+
steps:
27+
- name: Checkout repository
28+
uses: actions/checkout@v4
29+
30+
- name: Install dependencies (Linux)
31+
if: runner.os == 'Linux'
32+
run: |
33+
sudo apt-get update -qq
34+
sudo apt-get install -y -qq libgit2-dev libzstd-dev
35+
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo gpg --dearmor -o /usr/share/keyrings/llvm-archive-keyring.gpg
36+
echo "deb [signed-by=/usr/share/keyrings/llvm-archive-keyring.gpg] http://apt.llvm.org/$(lsb_release -cs)/ llvm-toolchain-$(lsb_release -cs)-20 main" | sudo tee /etc/apt/sources.list.d/llvm.list
37+
sudo apt-get update -qq
38+
sudo apt-get install -y -qq libllvm20
39+
40+
- name: Install ARO (Linux)
41+
if: runner.os == 'Linux'
42+
run: |
43+
if [ "$ARO_VERSION" = "latest" ]; then
44+
DOWNLOAD_URL=$(curl -s https://api.github.com/repos/arolang/aro/releases/latest \
45+
| grep "browser_download_url.*${{ matrix.asset }}" \
46+
| cut -d '"' -f 4)
47+
else
48+
DOWNLOAD_URL="https://github.com/arolang/aro/releases/download/${ARO_VERSION}/${{ matrix.asset }}"
49+
fi
50+
curl -fsSL "$DOWNLOAD_URL" | tar xz
51+
sudo mv aro /usr/local/bin/
52+
sudo mv libARORuntime.a /usr/local/lib/
53+
aro --version
54+
55+
- name: Install ARO (macOS)
56+
if: runner.os == 'macOS'
57+
run: |
58+
brew tap arolang/aro
59+
brew install aro libgit2 llvm@20
60+
aro --version
61+
62+
- name: Check code
63+
run: aro check .
64+
65+
# ===========================================================================
66+
# Build — compile for macOS (signed) and Linux
67+
# ===========================================================================
68+
build-linux:
69+
needs: check
70+
runs-on: ubuntu-latest
71+
steps:
72+
- name: Checkout repository
73+
uses: actions/checkout@v4
74+
75+
- name: Install dependencies
76+
run: |
77+
sudo apt-get update -qq
78+
sudo apt-get install -y -qq libgit2-dev libzstd-dev
79+
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo gpg --dearmor -o /usr/share/keyrings/llvm-archive-keyring.gpg
80+
echo "deb [signed-by=/usr/share/keyrings/llvm-archive-keyring.gpg] http://apt.llvm.org/$(lsb_release -cs)/ llvm-toolchain-$(lsb_release -cs)-20 main" | sudo tee /etc/apt/sources.list.d/llvm.list
81+
sudo apt-get update -qq
82+
sudo apt-get install -y -qq libllvm20
83+
84+
- name: Install ARO
85+
run: |
86+
if [ "$ARO_VERSION" = "latest" ]; then
87+
DOWNLOAD_URL=$(curl -s https://api.github.com/repos/arolang/aro/releases/latest \
88+
| grep "browser_download_url.*aro-linux-amd64.tar.gz" \
89+
| cut -d '"' -f 4)
90+
else
91+
DOWNLOAD_URL="https://github.com/arolang/aro/releases/download/${ARO_VERSION}/aro-linux-amd64.tar.gz"
92+
fi
93+
curl -fsSL "$DOWNLOAD_URL" | tar xz
94+
sudo mv aro /usr/local/bin/
95+
sudo mv libARORuntime.a /usr/local/lib/
96+
97+
- name: Build
98+
run: aro build . --optimize --strip --size -o sf
99+
100+
- name: Upload artifact
101+
uses: actions/upload-artifact@v4
102+
with:
103+
name: sf-linux-amd64
104+
path: sf
105+
106+
build-macos:
107+
needs: check
108+
runs-on: macos-latest
109+
steps:
110+
- name: Checkout repository
111+
uses: actions/checkout@v4
112+
113+
- name: Install ARO
114+
run: |
115+
brew tap arolang/aro
116+
brew install aro
117+
118+
- name: Import Code Signing Certificate
119+
if: github.ref_type == 'tag'
120+
env:
121+
APPLE_CERTIFICATE_BASE64: ${{ secrets.APPLE_CERTIFICATE_BASE64 }}
122+
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
123+
run: |
124+
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
125+
KEYCHAIN_PASSWORD=$(openssl rand -base64 32)
126+
127+
echo "$APPLE_CERTIFICATE_BASE64" | base64 --decode > certificate.p12
128+
129+
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
130+
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
131+
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
132+
133+
security import certificate.p12 \
134+
-P "$APPLE_CERTIFICATE_PASSWORD" \
135+
-A \
136+
-t cert \
137+
-f pkcs12 \
138+
-k $KEYCHAIN_PATH
139+
140+
security list-keychain -d user -s $KEYCHAIN_PATH
141+
security set-key-partition-list \
142+
-S apple-tool:,apple: \
143+
-s \
144+
-k "$KEYCHAIN_PASSWORD" \
145+
$KEYCHAIN_PATH
146+
147+
rm certificate.p12
148+
149+
- name: Build (signed)
150+
if: github.ref_type == 'tag'
151+
env:
152+
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
153+
run: |
154+
aro build . --sign "$APPLE_TEAM_ID" --optimize --strip --size -o sf
155+
codesign --verify --verbose sf
156+
157+
- name: Build (unsigned)
158+
if: github.ref_type != 'tag'
159+
run: aro build . --optimize --strip --size -o sf
160+
161+
- name: Notarize Binary
162+
if: github.ref_type == 'tag'
163+
env:
164+
APPLE_ID: ${{ secrets.APPLE_ID }}
165+
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
166+
APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }}
167+
run: |
168+
zip -r sf.zip sf
169+
xcrun notarytool submit sf.zip \
170+
--apple-id "$APPLE_ID" \
171+
--team-id "$APPLE_TEAM_ID" \
172+
--password "$APPLE_APP_PASSWORD" \
173+
--wait
174+
# Staple may fail for CLI tools — that's OK
175+
xcrun stapler staple sf || true
176+
177+
- name: Upload artifact
178+
uses: actions/upload-artifact@v4
179+
with:
180+
name: sf-macos-arm64
181+
path: sf
182+
183+
# ===========================================================================
184+
# Release — create GitHub release on tag push
185+
# ===========================================================================
186+
release:
187+
name: Create Release
188+
needs: [build-linux, build-macos]
189+
runs-on: ubuntu-latest
190+
if: github.ref_type == 'tag'
191+
permissions:
192+
contents: write
193+
steps:
194+
- name: Checkout repository
195+
uses: actions/checkout@v4
196+
197+
- name: Download all artifacts
198+
uses: actions/download-artifact@v4
199+
with:
200+
path: artifacts
201+
202+
- name: Package release assets
203+
run: |
204+
cd artifacts/sf-linux-amd64
205+
chmod +x sf
206+
tar -czvf ../sf-linux-amd64.tar.gz sf
207+
cd ../..
208+
209+
cd artifacts/sf-macos-arm64
210+
chmod +x sf
211+
tar -czvf ../sf-macos-arm64.tar.gz sf
212+
cd ../..
213+
214+
- name: Extract version from tag
215+
id: version
216+
run: |
217+
VERSION="${{ github.ref_name }}"
218+
VERSION="${VERSION#v}"
219+
echo "version=${VERSION}" >> $GITHUB_OUTPUT
220+
221+
- name: Create GitHub Release
222+
uses: softprops/action-gh-release@v2
223+
with:
224+
name: sf ${{ steps.version.outputs.version }}
225+
draft: false
226+
prerelease: ${{ contains(github.ref_name, '-') }}
227+
generate_release_notes: true
228+
files: |
229+
artifacts/sf-linux-amd64.tar.gz
230+
artifacts/sf-macos-arm64.tar.gz
231+
body: |
232+
## ShowFiles ${{ steps.version.outputs.version }}
233+
234+
A Midnight Commander-style terminal file manager written in ARO.
235+
236+
### Downloads
237+
238+
| Platform | Architecture | Download |
239+
|----------|--------------|----------|
240+
| macOS | Apple Silicon | [sf-macos-arm64.tar.gz](https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/sf-macos-arm64.tar.gz) |
241+
| Linux | x86_64 | [sf-linux-amd64.tar.gz](https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/sf-linux-amd64.tar.gz) |
242+
243+
### Installation
244+
245+
**macOS (Homebrew)**
246+
```bash
247+
brew tap arolang/applications
248+
brew install sf
249+
```
250+
251+
**macOS (Manual)**
252+
```bash
253+
curl -fsSL https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/sf-macos-arm64.tar.gz | tar xz
254+
sudo mv sf /usr/local/bin/
255+
```
256+
257+
**Linux**
258+
```bash
259+
curl -fsSL https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/sf-linux-amd64.tar.gz | tar xz
260+
sudo mv sf /usr/local/bin/
261+
```
262+
263+
# ======================================================================
264+
# Update Homebrew Formula
265+
# ======================================================================
266+
- name: Update Homebrew Formula
267+
continue-on-error: true
268+
env:
269+
HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
270+
run: |
271+
SHA256_MACOS=$(shasum -a 256 artifacts/sf-macos-arm64.tar.gz | awk '{print $1}')
272+
SHA256_LINUX=$(shasum -a 256 artifacts/sf-linux-amd64.tar.gz | awk '{print $1}')
273+
VERSION="${{ steps.version.outputs.version }}"
274+
REPO="${{ github.repository }}"
275+
TAG="${{ github.ref_name }}"
276+
277+
echo "Version: $VERSION"
278+
echo "SHA256 macOS: $SHA256_MACOS"
279+
echo "SHA256 Linux: $SHA256_LINUX"
280+
281+
git clone https://x-access-token:${HOMEBREW_TAP_TOKEN}@github.com/arolang/homebrew-applications.git
282+
cd homebrew-applications
283+
mkdir -p Formula
284+
285+
{
286+
echo 'class Sf < Formula'
287+
echo ' desc "Midnight Commander-style terminal file manager written in ARO"'
288+
echo " homepage \"https://github.com/${REPO}\""
289+
echo " version \"${VERSION}\""
290+
echo ' license "MIT"'
291+
echo ''
292+
echo ' on_macos do'
293+
echo ' depends_on arch: :arm64'
294+
echo " url \"https://github.com/${REPO}/releases/download/${TAG}/sf-macos-arm64.tar.gz\""
295+
echo " sha256 \"${SHA256_MACOS}\""
296+
echo ' end'
297+
echo ''
298+
echo ' on_linux do'
299+
echo " url \"https://github.com/${REPO}/releases/download/${TAG}/sf-linux-amd64.tar.gz\""
300+
echo " sha256 \"${SHA256_LINUX}\""
301+
echo ' end'
302+
echo ''
303+
echo ' def install'
304+
echo ' bin.install "sf"'
305+
echo ' end'
306+
echo ''
307+
echo ' test do'
308+
echo ' assert_match version.to_s, shell_output("#{bin}/sf --version")'
309+
echo ' end'
310+
echo 'end'
311+
} > Formula/sf.rb
312+
313+
git config user.name "github-actions[bot]"
314+
git config user.email "github-actions[bot]@users.noreply.github.com"
315+
316+
git add Formula/sf.rb
317+
if git diff --staged --quiet; then
318+
echo "No changes to formula"
319+
else
320+
git commit -m "chore: update sf to version ${VERSION}"
321+
git push https://x-access-token:${HOMEBREW_TAP_TOKEN}@github.com/arolang/homebrew-applications.git main
322+
fi

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/sf
2+
/.idea
3+
/.DS_Store

README.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# ShowFiles (sf)
2+
3+
A Midnight Commander-style terminal file manager written in [ARO](https://arolang.github.io/aro/).
4+
5+
![ShowFiles screenshot](assets/screen.jpg)
6+
7+
## Features
8+
9+
- Dual-panel layout: file list on the left, file preview on the right
10+
- Navigate with arrow keys (up/down to select, Enter to open directories)
11+
- Scrollable file list and preview panel
12+
- Streams only the first bytes of files for fast previews
13+
14+
## Installation
15+
16+
**macOS (Homebrew)**
17+
```bash
18+
brew tap arolang/applications
19+
brew install sf
20+
```
21+
22+
**Linux / macOS (Manual)**
23+
24+
Download the latest release from the [Releases](https://github.com/arolang/ShowFiles/releases) page, extract, and move to your PATH:
25+
```bash
26+
sudo mv sf /usr/local/bin/
27+
```
28+
29+
## Building from Source
30+
31+
Requires the ARO toolchain (`aro` CLI). Install it via `brew tap arolang/aro && brew install aro` or from [GitHub releases](https://github.com/arolang/aro/releases).
32+
33+
```bash
34+
# Syntax check
35+
aro check .
36+
37+
# Run directly (interpreter mode)
38+
aro run .
39+
40+
# Compile to native binary
41+
aro build . --optimize
42+
```
43+
44+
The compiled binary is placed in `.build/`.
45+
46+
## Project Structure
47+
48+
```
49+
sf/
50+
├── main.aro # Application startup and initial state
51+
├── handlers.aro # Keyboard input handlers
52+
├── observer.aro # Repository observers (reactive UI rendering)
53+
├── openapi.yaml # Schema definitions for UIState and FileItem
54+
└── templates/
55+
└── display.screen # Terminal screen template
56+
```

assets/screen.jpg

547 KB
Loading

0 commit comments

Comments
 (0)