Skip to content

Commit 4a0681b

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 4a0681b

9 files changed

Lines changed: 817 additions & 0 deletions

File tree

.github/workflows/build.yml

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

assets/screen.jpg

547 KB
Loading

0 commit comments

Comments
 (0)