Skip to content

Commit 9ebe048

Browse files
committed
feat: initial release of tmignore
Rust CLI that excludes developer dependency directories and arbitrary paths from macOS Time Machine backups using sticky exclusions. - 40 built-in dependency patterns (node_modules, target, vendor, etc.) - Built-in exclude paths for toolchains, Homebrew, Nix, Docker, Xcode - Configurable via ~/.config/tmignore/config.toml - LaunchAgent service for automatic background runs - CI/CD with code signing, notarization, and Homebrew tap
0 parents  commit 9ebe048

File tree

14 files changed

+2291
-0
lines changed

14 files changed

+2291
-0
lines changed

.github/workflows/release.yml

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
name: release
2+
3+
on:
4+
release:
5+
types: [created]
6+
7+
permissions:
8+
contents: write
9+
10+
jobs:
11+
release:
12+
name: Release - ${{ matrix.platform.release_for }}
13+
strategy:
14+
matrix:
15+
platform:
16+
- release_for: macOS-x86_64
17+
os: macOS-latest
18+
target: x86_64-apple-darwin
19+
bin: tmignore
20+
name: tmignore-Darwin-x86_64.tar.gz
21+
22+
- release_for: macOS-aarch64
23+
os: macOS-latest
24+
target: aarch64-apple-darwin
25+
bin: tmignore
26+
name: tmignore-Darwin-aarch64.tar.gz
27+
28+
runs-on: ${{ matrix.platform.os }}
29+
30+
steps:
31+
- name: Checkout
32+
uses: actions/checkout@v4
33+
34+
- name: Build binary
35+
uses: houseabsolute/actions-rust-cross@v0
36+
with:
37+
target: ${{ matrix.platform.target }}
38+
args: "--locked --release"
39+
strip: true
40+
41+
- name: Import code signing certificate
42+
env:
43+
APPLE_CERTIFICATE_BASE64: ${{ secrets.APPLE_CERTIFICATE_BASE64 }}
44+
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
45+
run: |
46+
CERTIFICATE_PATH=$RUNNER_TEMP/certificate.p12
47+
KEYCHAIN_PATH=$RUNNER_TEMP/signing.keychain-db
48+
KEYCHAIN_PASSWORD=$(openssl rand -base64 32)
49+
50+
echo -n "$APPLE_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH
51+
52+
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
53+
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
54+
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
55+
security import $CERTIFICATE_PATH -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
56+
security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
57+
security list-keychain -d user -s $KEYCHAIN_PATH
58+
59+
- name: Sign binary
60+
run: |
61+
codesign --force --options runtime \
62+
--sign "Developer ID Application: Wassim Metallaoui (${{ secrets.APPLE_TEAM_ID }})" \
63+
target/${{ matrix.platform.target }}/release/${{ matrix.platform.bin }}
64+
65+
- name: Notarize binary
66+
continue-on-error: true
67+
timeout-minutes: 15
68+
env:
69+
APPLE_ID: ${{ secrets.APPLE_ID }}
70+
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
71+
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
72+
run: |
73+
ZIP_PATH=$RUNNER_TEMP/${{ matrix.platform.bin }}-notarize.zip
74+
ditto -c -k target/${{ matrix.platform.target }}/release/${{ matrix.platform.bin }} "$ZIP_PATH"
75+
xcrun notarytool submit "$ZIP_PATH" \
76+
--apple-id "$APPLE_ID" \
77+
--password "$APPLE_ID_PASSWORD" \
78+
--team-id "$APPLE_TEAM_ID" \
79+
--wait
80+
81+
- name: Archive binary
82+
shell: bash
83+
run: |
84+
cd target/${{ matrix.platform.target }}/release
85+
tar czvf ../../../${{ matrix.platform.name }} ${{ matrix.platform.bin }}
86+
cd -
87+
88+
- name: Generate SHA-256
89+
shell: bash
90+
run: |
91+
shasum -a 256 ${{ matrix.platform.name }} > ${{ matrix.platform.name }}-sha.txt
92+
93+
- name: Publish release artifacts
94+
uses: actions/upload-artifact@v4
95+
with:
96+
name: tmignore-${{ matrix.platform.target }}
97+
path: "tmignore-*"
98+
99+
- name: Publish GitHub release
100+
uses: softprops/action-gh-release@v2
101+
with:
102+
files: "tmignore*"
103+
104+
- name: Clean up keychain
105+
if: always()
106+
run: security delete-keychain $RUNNER_TEMP/signing.keychain-db
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
name: update-homebrew
2+
3+
on:
4+
workflow_run:
5+
workflows: [release]
6+
types: [completed]
7+
8+
jobs:
9+
update-formula:
10+
if: ${{ github.event.workflow_run.conclusion == 'success' }}
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Get release version
15+
id: version
16+
run: |
17+
TAG=$(gh release view --repo wassimk/tmignore --json tagName -q '.tagName')
18+
VERSION="${TAG#v}"
19+
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
20+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
21+
env:
22+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
23+
24+
- name: Download SHA-256 checksums
25+
run: |
26+
gh release download "${{ steps.version.outputs.tag }}" \
27+
--repo wassimk/tmignore \
28+
--pattern '*-sha.txt' \
29+
--dir checksums
30+
env:
31+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
32+
33+
- name: Parse checksums
34+
id: shas
35+
run: |
36+
for file in checksums/*-sha.txt; do
37+
SHA=$(awk '{print $1}' "$file")
38+
NAME=$(basename "$file" -sha.txt)
39+
40+
case "$NAME" in
41+
tmignore-Darwin-aarch64.tar.gz) echo "darwin_arm64=$SHA" >> "$GITHUB_OUTPUT" ;;
42+
tmignore-Darwin-x86_64.tar.gz) echo "darwin_x86_64=$SHA" >> "$GITHUB_OUTPUT" ;;
43+
esac
44+
done
45+
46+
- name: Checkout homebrew-tap
47+
uses: actions/checkout@v4
48+
with:
49+
repository: wassimk/homebrew-tap
50+
token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
51+
52+
- name: Update formula
53+
run: |
54+
cat > Formula/tmignore.rb << 'RUBY'
55+
class Tmignore < Formula
56+
desc "Exclude developer dependency directories and arbitrary paths from macOS backups"
57+
homepage "https://github.com/wassimk/tmignore"
58+
version "${{ steps.version.outputs.version }}"
59+
license "MIT"
60+
61+
depends_on :macos
62+
63+
if Hardware::CPU.arm?
64+
url "https://github.com/wassimk/tmignore/releases/download/v#{version}/tmignore-Darwin-aarch64.tar.gz"
65+
sha256 "${{ steps.shas.outputs.darwin_arm64 }}"
66+
elsif Hardware::CPU.intel?
67+
url "https://github.com/wassimk/tmignore/releases/download/v#{version}/tmignore-Darwin-x86_64.tar.gz"
68+
sha256 "${{ steps.shas.outputs.darwin_x86_64 }}"
69+
end
70+
71+
def install
72+
bin.install "tmignore"
73+
end
74+
75+
def caveats
76+
<<~EOS
77+
To start tmignore as a background service (runs every 24 hours):
78+
tmignore install
79+
80+
To remove the background service:
81+
tmignore uninstall
82+
83+
To generate a config file:
84+
tmignore init
85+
86+
Config: ~/.config/tmignore/config.toml
87+
EOS
88+
end
89+
90+
test do
91+
assert_match version.to_s, shell_output("#{bin}/tmignore --version")
92+
end
93+
end
94+
RUBY
95+
96+
- name: Remove leading whitespace from formula
97+
run: sed -i 's/^ //' Formula/tmignore.rb
98+
99+
- name: Commit and push
100+
run: |
101+
git config user.name "github-actions[bot]"
102+
git config user.email "github-actions[bot]@users.noreply.github.com"
103+
git add Formula/tmignore.rb
104+
git diff --cached --quiet && echo "No changes to commit" && exit 0
105+
git commit -m "feat: update tmignore to ${{ steps.version.outputs.version }}"
106+
git push

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/target

0 commit comments

Comments
 (0)