Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
32 changes: 25 additions & 7 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,28 @@ on:
push:
tags:
- "*"
workflow_dispatch:
inputs:
dry_run:
description: "Build and package only (no GitHub Release)."
required: false
default: "true"

jobs:
build:
runs-on: ubuntu-latest
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- name: Install Composer dependencies
uses: php-actions/composer@v6
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
php_version: "8.2"
go-version: "1.22"
- name: Build search binary
run: |
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags="-s -w" -o search_amd64 .
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags="-s -w" -o search_arm64 .
lipo -create -output search search_amd64 search_arm64
rm -f search_amd64 search_arm64
# Store version number without `v`
- name: Write release version
run: |
Expand All @@ -24,12 +36,18 @@ jobs:
uses: com30n/build-alfred-workflow@v1
with:
workflow_dir: .
exclude_patterns: ".git/* .gitignore .github/* docker_tag Dockerfile-php-build DOCKER_ENV output.log resources/*"
exclude_patterns: ".git/* .gitignore .github/* build-binary.sh go.mod *.go *_test.go resources/* search_amd64 search_arm64 target/*"
custom_version: "${{ env.VERSION }}"
- name: Create release
id: create_release
uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/')
if: startsWith(github.ref, 'refs/tags/') && (github.event_name != 'workflow_dispatch' || github.event.inputs.dry_run != 'true')
with:
token: ${{ secrets.RELEASE_TOKEN }}
files: ${{ steps.alfred_builder.outputs.workflow_file }}
- name: Upload workflow artifact
if: github.event_name == 'workflow_dispatch'
uses: actions/upload-artifact@v4
with:
name: alfred-workflow
path: ${{ steps.alfred_builder.outputs.workflow_file }}
17 changes: 5 additions & 12 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,13 @@ on:
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php: [8.2, 8.3, 8.4, 8.5]
name: PHP ${{ matrix.php }}
name: Go tests
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
- name: Setup Go
uses: actions/setup-go@v5
with:
php-version: ${{ matrix.php }}
extensions: json, curl
- name: Install dependencies
run: composer update --prefer-dist --no-interaction --no-progress ${{ matrix.php == '8.5' && '--ignore-platform-req=php' || '' }}
go-version: "1.22"
- name: Run tests
run: composer test
run: go test ./...
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
.idea
.DS_Store
vendor/
composer.lock
search
search_amd64
search_arm64
target/
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Changed

- Rewrote the workflow in Go.

## [0.0.5] - 2025-04-04

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This Alfred workflow fuzzy-searches your development directories so you can quickly open projects in VS Code, PhpStorm, iTerm, or Finder.

It’s heavily tailored to how I use it, but you can change the editors quickly and even dig into `search.php` if you’d like to adjust the configuration of the underlying [Fuze](https://github.com/Loilo/Fuse) library.
It now runs as a compiled Go binary, and you can adjust the fuzzy matching or editor icons in `main.go` if you want to customize behavior.

![Workflow screenshot](resources/screenshot.png)

Expand Down
60 changes: 60 additions & 0 deletions alfred.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package main

const (
iconTypeFile = "fileicon"
)

const (
vsCodeApp = "/Applications/Visual Studio Code.app"
phpStormApp = "/Applications/PhpStorm.app"
iTermApp = "/Applications/iTerm.app"
finderApp = "/System/Library/CoreServices/Finder.app"
)

type alfredOutput struct {
Items []alfredItem `json:"items"`
}

type alfredItem struct {
Title string `json:"title"`
Subtitle string `json:"subtitle,omitempty"`
Arg string `json:"arg,omitempty"`
Icon *alfredIcon `json:"icon,omitempty"`
Mods *alfredMods `json:"mods,omitempty"`
}

type alfredMods struct {
Cmd *alfredMod `json:"cmd,omitempty"`
Shift *alfredMod `json:"shift,omitempty"`
Ctrl *alfredMod `json:"ctrl,omitempty"`
}

type alfredMod struct {
Icon *alfredIcon `json:"icon,omitempty"`
}

type alfredIcon struct {
Path string `json:"path"`
Type string `json:"type,omitempty"`
}

func newAlfredItem(item listItem) alfredItem {
return alfredItem{
Title: item.Folder,
Subtitle: item.Path,
Arg: item.Path,
Icon: iconForFilePath(vsCodeApp),
Mods: &alfredMods{
Cmd: &alfredMod{Icon: iconForFilePath(phpStormApp)},
Shift: &alfredMod{Icon: iconForFilePath(iTermApp)},
Ctrl: &alfredMod{Icon: iconForFilePath(finderApp)},
},
}
}

func iconForFilePath(path string) *alfredIcon {
return &alfredIcon{
Path: path,
Type: iconTypeFile,
}
}
11 changes: 11 additions & 0 deletions build-binary.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -euo pipefail

if command -v lipo >/dev/null 2>&1; then
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags="-s -w" -o search_amd64 .
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags="-s -w" -o search_arm64 .
lipo -create -output search search_amd64 search_arm64
rm -f search_amd64 search_arm64
else
go build -ldflags="-s -w" -o search .
fi
33 changes: 0 additions & 33 deletions composer.json

This file was deleted.

3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/mattstein/alfred-quick-open-project-workflow

go 1.22
35 changes: 35 additions & 0 deletions helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package main

import "strings"

func buildSearchPathString(settingValue, homePath string) string {
paths := parseSearchPaths(settingValue, homePath)
return "{" + strings.Join(paths, ",") + "}/*"
}

func parseSearchPaths(settingValue, homePath string) []string {
raw := strings.Split(settingValue, ",")
paths := make([]string, 0, len(raw))
for _, path := range raw {
path = strings.TrimSpace(path)
path = expandTilde(path, homePath)
paths = append(paths, path)
}
return paths
}

func expandTilde(path, homePath string) string {
if strings.HasPrefix(path, "~/") {
return homePath + "/" + strings.TrimPrefix(path, "~/")
}
return path
}

func splitAndTrim(value string) []string {
raw := strings.Split(value, ",")
out := make([]string, 0, len(raw))
for _, item := range raw {
out = append(out, strings.TrimSpace(item))
}
return out
}
31 changes: 0 additions & 31 deletions helpers.php

This file was deleted.

65 changes: 65 additions & 0 deletions helpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package main

import "testing"

func TestBuildSearchPathString(t *testing.T) {
tests := []struct {
name string
paths string
home string
expected string
}{
{
name: "single path",
paths: "/Projects",
home: "/Users/test",
expected: "{/Projects}/*",
},
{
name: "multiple paths",
paths: "/Projects,/Work",
home: "/Users/test",
expected: "{/Projects,/Work}/*",
},
{
name: "tilde expansion",
paths: "~/Projects",
home: "/Users/test",
expected: "{/Users/test/Projects}/*",
},
{
name: "tilde expansion in multiple paths",
paths: "~/Projects,~/Work",
home: "/Users/test",
expected: "{/Users/test/Projects,/Users/test/Work}/*",
},
{
name: "mixed absolute and tilde paths",
paths: "~/Projects,/var/www",
home: "/Users/test",
expected: "{/Users/test/Projects,/var/www}/*",
},
{
name: "trims whitespace",
paths: " /Projects , /Work ",
home: "/Users/test",
expected: "{/Projects,/Work}/*",
},
{
name: "trims whitespace and expands tilde",
paths: " ~/Projects ",
home: "/Users/test",
expected: "{/Users/test/Projects}/*",
},
}

for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
result := buildSearchPathString(test.paths, test.home)
if result != test.expected {
t.Fatalf("expected %q, got %q", test.expected, result)
}
})
}
}
2 changes: 1 addition & 1 deletion info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@
<key>runningsubtext</key>
<string>Hang on...</string>
<key>script</key>
<string>php search.php {query}</string>
<string>./search {query}</string>
<key>scriptargtype</key>
<integer>0</integer>
<key>scriptfile</key>
Expand Down
Loading
Loading