Skip to content

Commit 29c793f

Browse files
authored
Initial release
1 parent 5639e85 commit 29c793f

9 files changed

Lines changed: 140 additions & 118 deletions

File tree

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
uses: actions/checkout@v4
1616

1717
- name: Build
18-
run: chmod +x build.sh && ./build.sh
18+
run: chmod +x build && ./build
1919
env:
2020
APP_VERSION: ${{ github.ref_name }}
2121

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
.DS_Store
22
/.build
3+
/bin
34
/Packages
45
xcuserdata/
56
DerivedData/
67
.swiftpm/configuration/registries.json
78
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
89
.netrc
10+
/Sources/**/generated*
11+
.vscode/launch.json

.vscode/launch.json

Lines changed: 0 additions & 64 deletions
This file was deleted.

Package.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,18 @@ import PackageDescription
55
let package = Package(
66
name: "aliastools",
77
targets: [
8+
.target(
9+
name: "config",
10+
path: "Sources/config"
11+
),
812
.executableTarget(
913
name: "readalias",
14+
dependencies: ["config"],
1015
path: "Sources/readalias"
1116
),
1217
.executableTarget(
1318
name: "mkalias",
19+
dependencies: ["config"],
1420
path: "Sources/mkalias"
1521
),
1622
],

README.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Alias Tools
2+
3+
Tools for working with MacOS alias files.
4+
5+
Includes two universal binaries: `readalias` and `mkalias`,
6+
for resolving and creating alias files.
7+
8+
Neither is very thouroughly tested.
9+
10+
I wrote these utilities for my own needs, on Tahoe 26.2,
11+
but they likely work on some earlier verions of macOS.
12+
13+
## Installation
14+
15+
Download the [latest release](https://github.com/heckman/macOS-alias-tools/releases/latest)
16+
and put the two universal binaries somewhere on your `PATH`.
17+
18+
Alternatively, build the binaries from source.
19+
You will neet to have Command Line Tools (CLT) installed,
20+
which can be installed with `xcode-select --install`.
21+
Clone [the repository](https://github.com/heckman/macOS-alias-tools),
22+
and change your current directory to its root.
23+
From there, run `./build`.
24+
This should produce the `readalias` and `mkalias` binaries
25+
in a `./bin` directory.
26+
27+
## Features
28+
29+
At the moment, `readalias` simply prints the path to the original item,
30+
and has no options beyond `--help` and `--version`,
31+
while `mkalias` can be given the `force` ( or `-f`) option
32+
indicating existing files should be overwritten--by default it
33+
is non-destructive.
34+
35+
Both respond well to the `--help` option, producing output which
36+
includes descriptions their various exit codes.
37+
38+
### Future features
39+
40+
By default `mkalias` will fail if the file specified for
41+
the new alias already exists, adn will overwrite it if the
42+
destructive behaviour is selected with the `force` (or `-f`) option.
43+
44+
I indend to offer a third behaviour, which, when the
45+
specified name for the alias is taken, generates a new name
46+
in the way the Finder does. This might become the default
47+
behaviour in which case we will need a new a command-line
48+
option for the fail-on-existing file behaviour.
49+
50+
The renaming strategy is this: Use the same name as the original,
51+
if avaialbe, otherwise append ` alias` to the name. If that
52+
is still not unique, then append the smallest integer
53+
greater than one, separated by a space, sufficient to
54+
generate a unique name.
55+
56+
### Edge Cases
57+
58+
These tools have only been nominally tested,
59+
and most edge cases have not been explored:
60+
61+
- Broken Aliases
62+
- Aliases to unmounted external/network volumes
63+
- Creating and Alias to another Alias
64+
65+
## Props where due
66+
67+
Prior to writing these two tools,
68+
I was using _alisma_, by Howard Oakley,
69+
which you can find on his site
70+
[eclecticlight.co](https://eclecticlight.co/)
71+
where he shares a great number of useful tools.
72+
73+
I found myself wrapping _alisma_ in shell functions to
74+
provide interfaces that felt more natural to me,
75+
so I decided to use the same system calls as _alisma_
76+
to make two new programs: `readalias` and `mkalias`.

Sources/mkalias/main.swift

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,31 @@
11
import Foundation
2+
import config
23

34
// Copyright 2026 Erik Heckman <erik@heckman.ca>
45
// SPDX-License-Identifier: MIT
56
//
67
// mkalias
78

8-
#if VERSION
9-
let version = "mkalias v\(VERSION)"
10-
#else
11-
let version = "mkalias v0.0.1" // Fallback for local dev
12-
#endif
13-
149
let usage = """
15-
Usage: mkalias [-f|--force] <path-of-original> <alias-file>
16-
mkalias -h|--help
10+
Usage: mkalias [-f|--force] [--] <path-of-original> <alias-file>
11+
mkalias -h|-v|--help|--version
1712
"""
1813
let help = """
1914
Create an alias file that points to the path of an original
2015
filesystem object.
2116
22-
Will not overwrite and existing file with <alias-file> unless
23-
the `--force` (or `-f`) option is specified.
24-
25-
On success, create the alias, print nothing, and exit with a
26-
zero exit status.
17+
Will not overwrite and existing file with <alias-file>
18+
unless the `--force` (or `-f`) option is specified.
19+
Will never overwrite an existing directory.
2720
28-
On failure, print an error message to stderr and exit
29-
with a non-zero exit status: 2 if the file is not specified,
21+
On success, create the alias, print nothing,
22+
and exit with a status of zero.
3023
31-
On failure, print an error message to stderr and exit
32-
with a non-zero exit status: 2 if the file is not specified,
33-
not found, or not an alias file; 1 if the alias cannot be
34-
resolved; 71 on an unexpected file-system error.
24+
On failure, print an error message to stderr and terminate
25+
with a non-zero exit status: 3 when refusing to clobber
26+
something already at <alias-file>; 2 when <path-of-original>
27+
does not exist, invalid options are specified, or not enough
28+
arguments are provided; 1 when faling to create the alias.
3529
"""
3630

3731
do {
@@ -94,14 +88,14 @@ do {
9488
"Error: cannot overwrite directory: \(aliasFile)\n",
9589
stderr
9690
)
97-
exit(2)
91+
exit(3)
9892
}
9993
} else {
10094
fputs(
10195
"Error: \(isDir.boolValue ? "directory" : "file") exists: \(aliasFile)\n",
10296
stderr
10397
)
104-
exit(2)
98+
exit(3)
10599
}
106100

107101
}

Sources/readalias/main.swift

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,22 @@
11
import Foundation
2+
import config
23

34
// Copyright 2026 Erik Heckman <erik@heckman.ca>
45
// SPDX-License-Identifier: MIT
56
//
67
// readalias
78

8-
#if VERSION
9-
let version = "readalias v\(VERSION)"
10-
#else
11-
let version = "readalias v0.0.1" // Fallback for local dev
12-
#endif
13-
149
let usage = """
1510
Usage: readalias <alias-file>
16-
readalias -h|--help
11+
readalias -h|-v|--help|--version
1712
"""
1813
let help = """
1914
Print the path to the original file of an alias file.
20-
On failure, print an error message to stderr and exit
21-
with a non-zero exit status: 2 if the file is not specified,
22-
not found, or not an alias file; 1 if the alias cannot be
23-
resolved; 71 on an unexpected file-system error.
15+
16+
On failure, print an error message to stderr and exit with
17+
a non-zero exit status: 2 if the file is not specified,
18+
not found, or not an alias file; 1 if the alias cannot
19+
be resolved; 71 on an unexpected file-system error.
2420
"""
2521

2622
do {

build

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/bin/zsh
2+
set -e
3+
4+
cd ${0:h}
5+
6+
print "Cleaning old build files..."
7+
rm -rf .build bin
8+
rm -f Sources/config/generated.*(N)
9+
10+
VERSION="${${APP_VERSION:-$(git describe --tags --always --dirty --long || echo 0.0.1)}#v}"
11+
print "Building universal binaries for version: $VERSION"
12+
13+
mkdir -p Sources/config
14+
cat <<EOF > Sources/config/generated.version.swift
15+
// Generated by build script. Should be ignored by Git.
16+
public let version = "$VERSION"
17+
EOF
18+
19+
swift build -c release --arch arm64 --arch x86_64
20+
21+
if [[ -t 1 ]]
22+
then
23+
print "\nBuild complete!"
24+
print "Location: ${0:A:h}/.build/apple/Products/Release/"
25+
ls -lh "${0:A:h}/.build/apple/Products/Release/"{readalias,mkalias}
26+
print "Copying binaries to ${0:A:h}/bin/"
27+
mkdir -p bin
28+
cp \
29+
.build/apple/Products/Release/readalias \
30+
.build/apple/Products/Release/mkalias \
31+
bin
32+
fi

build.sh

Lines changed: 0 additions & 21 deletions
This file was deleted.

0 commit comments

Comments
 (0)