Skip to content

Conversation

@n8henrie
Copy link
Member

@n8henrie n8henrie commented Oct 23, 2025

  • init qs-push-plugin.swift
  • Don't delete anything, error out instead.
  • Remove reference to checkIsUniversalBinary

Many moons ago we had discussed reimplementing qs-push-plugin in Python.

I decided to give a shot to having ChatGPT 5 do it for us but in Swift.

Reasons I think swift is a reasonable choice:

  • swift is pretty darn verbose, which is a bit of a pain for writing but ends up being fairly readable
  • with a chmod and proper shebang it is runnable as a standalone file, just like a python / ruby script
  • python dependency management is still a trainwreck (could probably write it stdlib, but urllib.request can be a mess for anything complicated)
  • going forward, Apple seems likely to ensure swift can run without requiring external dependencies
  • I think it's worth increasing our familiarity with swift

TODO:

  • squash prior to merge
  • rename qs-push-plugin.swift -> qs-push-plugin once this is ready (leaving both for easy comparisons)
  • tests for qs-push-plugin (maybe overkill)
  • squash commits to get rid of all those ugly "debug" and "make CI go brrr" commits
  • move the action to QS org
  • change the action to clone the latest tagged release of Quicksilver (currently clones this branch by necessity)
  • remove KEYCHAIN_PASSWORD from our org secrets (changed to generate and use a random password each run, passing in a fixed password seems to have no benefit since we just use it to initialize and then unlock a keychain)
  • search for straggling TODOs
  • test that the automated qs-push-plugin call works as expected (which automatically pushes a signed plugin to prod after a git tag, using the commit message body as the changelog)
  • check with @pjrobertson and @skurfer that the design decision of "commit message body" (which should be raw HTML content) for the --changes argument makes sense / is acceptable
  • ensure markdown parsing of the plugin changelog works

@n8henrie n8henrie force-pushed the qs-push-plugin.swift branch from 528449c to 0972460 Compare October 23, 2025 14:15
@n8henrie
Copy link
Member Author

n8henrie commented Oct 27, 2025

This grew into a bigger effort; I finally decided to try my hand at making our own custom GitHub Action for building plugins.

It's currently at https://github.com/n8henrie/qs-plugin-action but will be moved to the QS org shortly.

I figured the most sensible way to do that would be adding a new script to Tools/ to do the heavy lifting. In order to build a plugin we already have to clone QS, so there seems to be little/no downside to using a tool from the same repo.

Additional updates / changes:

  • did some cleanup with qsrelease build script / process:
    • the QS_BUILD_ONLY env var was problematic and behavior was not matching this description
      • I don't think our tests were actually running in CI recently due to the QS_BUILD_ONLY flag being set
      • changed to QS_DONT_TEST to make more explicit
    • removed the call to qssign at the end of qsrelease
      • for interactive development, signing is now generally handled in XCode by the Local Self-Signed stuff
      • we call qssign explicitly for release builds in CI, having it in qsrelease just made the logic harder
  • added some logic that automatically pushes a plugin to production (using my credentials -- maybe we should make a dedicated user for this?)
    • it uses the body (excluding the first line) of the commit message for a tag as the --changes argument to qs-push-plugin
    • as such, commit messages for tags should be formatted:
this is my commit message with whatever details

<ul>
<li>this is for the --changes argument</li>
<li>so is this</li>
</ul>

If this is deemed okay to merge, I'll need to make a few minor tweaks prior to merge:

EDIT: Moved todos to first post

Summary of changes:

qs-push-plugin:

- convert from ruby to Swift
- the initial version in this commit was generated by chatgpt 5 pro on
20251022 using the historical ruby implementation of qs-push-plugin as
input and the simple prompt `Convert the below Ruby script to a Swift
script that only uses the stdlib.`
- minor changes from the LLM output (e.g. dispatchGroup instead of semaphore)

.github/workflows/ci.yml, Quicksilver/Tools/qsrelease, Quicksilver/Tools/qssign, Quicksilver/Tools/qstest:

- some much needed maintenance as these have accumulated a little cruft in the last few years
- `QS_BUILD_ONLY` removed as functionality was duplicated with QS_DONT_SIGN
- `QS_DONT_SIGN` removed as we weren't automatically signing using `qsrelease`
  - signing happens via XCode if running interactively
  - previously CI was using `QS_DONT_SIGN` and manually calling `qssign` when appropriate
  - this change just makes the above more explicit by removing `qssign` from `qsrelease`, so `QS_DONT_SIGN` is no longer useful
- added `QS_DONT_TEST` which allows us to skip testing in `qsrelease`
  - this can be helpful for example when building plugins, as the Quicksilver build is done from a tagged release (for which we can be confident tests have already been run)
  - alternatively could consider mirroring the `qssign` changes and just make `qstest` a manual thing
- moved much of the signing logic from yaml to qssign
  - much easier to write and maintain with appropriate linters, formatters, highighting, etc.
- removed calls to set up the self-signed cert, as this process is not useful or needed in CI

Quicksilver/Quicksilver.xcodeproj/project.pbxproj:

- set signing identity to ad-hoc (`-`) for the testing scheme
  - our current tests do not require special permissions, therefore do not require the special cert

Quicksilver/Tools/utils.sh

- new file to hold shell functions that are useful to share between our other scripts
- should reduce duplicated code and help with readability and maintainability

Quicksilver/Tools/build-qs-plugin:

- new script to build a plugin
- given that the QS frameworks are required to build a plugin, we already clone the QS source as part of building a plugin
- therefore adding a script to the QS source lets us keep the bulk of the logic in one place
- works in conjunction with the new <https://github.com/quicksilver/qs-plugin-action>
- this lets us remove a *ton* of build logic from the CI scripts in each individual plugin repo; they can essentially be reduced to just passing in the required secrets and `use`ing the new action
@n8henrie n8henrie force-pushed the qs-push-plugin.swift branch from ad9affc to 8fcc8c7 Compare October 30, 2025 14:35
@n8henrie
Copy link
Member Author

Squashed and rebased.

Copying from the commit message:

CI and tooling rework for plugins

Summary of changes:

qs-push-plugin:

  • convert from ruby to Swift
  • the initial version in this commit was generated by chatgpt 5 pro on
    20251022 using the historical ruby implementation of qs-push-plugin as
    input and the simple prompt Convert the below Ruby script to a Swift script that only uses the stdlib.
  • minor changes from the LLM output (e.g. dispatchGroup instead of semaphore)

.github/workflows/ci.yml, Quicksilver/Tools/qsrelease, Quicksilver/Tools/qssign, Quicksilver/Tools/qstest:

  • some much needed maintenance as these have accumulated a little cruft in the last few years
  • QS_BUILD_ONLY removed as functionality was duplicated with QS_DONT_SIGN
  • QS_DONT_SIGN removed as we weren't automatically signing using qsrelease
    • signing happens via XCode if running interactively
    • previously CI was using QS_DONT_SIGN and manually calling qssign when appropriate
    • this change just makes the above more explicit by removing qssign from qsrelease, so QS_DONT_SIGN is no longer useful
  • added QS_DONT_TEST which allows us to skip testing in qsrelease
    • this can be helpful for example when building plugins, as the Quicksilver build is done from a tagged release (for which we can be confident tests have already been run)
    • alternatively could consider mirroring the qssign changes and just make qstest a manual thing
  • moved much of the signing logic from yaml to qssign
    • much easier to write and maintain with appropriate linters, formatters, highighting, etc.
  • removed calls to set up the self-signed cert, as this process is not useful or needed in CI

Quicksilver/Quicksilver.xcodeproj/project.pbxproj:

  • set signing identity to ad-hoc (-) for the testing scheme
    • our current tests do not require special permissions, therefore do not require the special cert

Quicksilver/Tools/utils.sh

  • new file to hold shell functions that are useful to share between our other scripts
  • should reduce duplicated code and help with readability and maintainability

Quicksilver/Tools/build-qs-plugin:

  • new script to build a plugin
  • given that the QS frameworks are required to build a plugin, we already clone the QS source as part of building a plugin
  • therefore adding a script to the QS source lets us keep the bulk of the logic in one place
  • works in conjunction with the new https://github.com/quicksilver/qs-plugin-action
  • this lets us remove a ton of build logic from the CI scripts in each individual plugin repo; they can essentially be reduced to just passing in the required secrets and useing the new action

@pjrobertson
Copy link
Member

I was actually thinking that we should add a step to the plugins CI that is 'upload to qsapp.com'. Funnily enough, last week I got claude to convert our ruby script to python and it worked fairly well. If swift is your thing and it works, then that's also good with me! I didn't realise #!/usr/bin/env swift was a scripting language. Cool

Using the commit message as the release notes seems fine to me, but if we're going down this route then I think we should probably make it a bit easier to write the commit messages in markdown and then have the script convert that to html probably easiest to do this in the CI script or qs-push-plugin, but could even happen server-side.

@n8henrie
Copy link
Member Author

Yeah I had originally planned to port to python, but frankly:

  • python dependency management is a dumpster fire without third-party tools
  • python network requests are a dumpster fire without third-party packages (at least anything beyond a simple GET)
  • Apple overall seems to be de-emphasizing python and has even said they even stop including it by default1

Based on that, I thought Swift script seemed like a reasonable compromise -- single-file, no compilation step, fairly readable, and Apple seems to continue investing in Swift.

I think we should probably make it a bit easier to write the commit messages in markdown and then have the script convert that to html

That's a great idea.

Footnotes

  1. https://developer.apple.com/documentation/macos-release-notes/macos-catalina-10_15-release-notes#Deprecations

For consistency with other tooling
@n8henrie
Copy link
Member Author

n8henrie commented Oct 30, 2025

Done: quicksilver/qs-plugin-action@d069df9

EDIT: I think it's set up in a way that it will just fail if the markdown parsing fails, in which case we'll just have to push it manually which seems fine.

But that reminds me that this part still needs to be tested (adding to the TODOs up top). @pjrobertson what do you think the best way to do that would be? I presume it will entail pushing an update to a plugin "for real" -- do you have a candidate? I had to give all our users a "this is a test" update :)

@n8henrie
Copy link
Member Author

n8henrie commented Oct 30, 2025

For context, our plugin CI should be able to simplify to just this: quicksilver/com.apple.Safari-qsplugin#11

@pjrobertson
Copy link
Member

But that reminds me that this part still needs to be tested (adding to the TODOs up top). @pjrobertson what do you think the best way to do that would be? I presume it will entail pushing an update to a plugin "for real" -- do you have a candidate? I had to give all our users a "this is a test" update :)

I don't have anything right now. You could try with the safari plugin to get a new build out :)

You can always delete a plugin manually from https://qs0.qsapp.com/plugins/admin/

Agreed we should probably set up a CI-specific username/password for this - I've messaged you one

@n8henrie
Copy link
Member Author

@pjrobertson @skurfer -- I just updated the Safari plugin to fix #3085

I pushed the update using this process and got the changelog from the git tag like we had discussed. Seemed to work!

I think this is ready for me to prepare for merging -- just need to remove a few branch-related workarounds so it will run on main and cut a release for the GitHub actions (so we have something stable to pin to if desired).

What do you think?

@pjrobertson
Copy link
Member

Everything here looks good to me. Feel free to merge! Next step will be to change the CI for all the plugins to use this action to actually push the plugin and release?

@n8henrie
Copy link
Member Author

n8henrie commented Dec 8, 2025

Yup, that's the idea! I'll merge soon and start working on plugin updates.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants