-
Notifications
You must be signed in to change notification settings - Fork 2
feat(release): add homebrew cask #794
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| // Generates the Homebrew cask for the Devsy desktop app from the released | ||
| // per-arch macOS disk images, embedding each image's sha256. | ||
| package main | ||
|
|
||
| import ( | ||
| "crypto/sha256" | ||
| "encoding/hex" | ||
| "flag" | ||
| "fmt" | ||
| "os" | ||
| "path/filepath" | ||
| "strings" | ||
| "text/template" | ||
| ) | ||
|
|
||
| type arch struct { | ||
| Cask string // cask arch token: arm, intel | ||
| DMG string // release asset filename | ||
| SHA256 string | ||
| } | ||
|
|
||
| var arches = []arch{ | ||
| {Cask: "arm", DMG: "Devsy_mac_arm64.dmg"}, | ||
| {Cask: "intel", DMG: "Devsy_mac_x64.dmg"}, | ||
| } | ||
|
|
||
| const caskTmpl = `cask "devsy" do | ||
| arch arm: "arm64", intel: "x64" | ||
|
|
||
| version "{{ .Version }}" | ||
| sha256 arm: "{{ (index .Arches "arm").SHA256 }}", | ||
| intel: "{{ (index .Arches "intel").SHA256 }}" | ||
|
|
||
| url "https://github.com/{{ .Repo }}/releases/download/v#{version}/Devsy_mac_#{arch}.dmg" | ||
| name "Devsy" | ||
| desc "Standardized dev workspaces across Docker, Kubernetes, cloud, and SSH" | ||
| homepage "https://www.devsy.sh" | ||
|
|
||
| app "Devsy.app" | ||
|
|
||
| zap trash: [ | ||
| "~/Library/Application Support/Devsy", | ||
| "~/Library/Logs/Devsy", | ||
| "~/Library/Preferences/sh.devsy.app.plist", | ||
| "~/Library/Saved Application State/sh.devsy.app.savedState", | ||
| ] | ||
| end | ||
| ` | ||
|
|
||
| func fileSHA256(path string) (string, error) { | ||
| data, err := os.ReadFile(path) // #nosec G304 -- release tooling reads local build artifacts. | ||
| if err != nil { | ||
| return "", err | ||
| } | ||
| sum := sha256.Sum256(data) | ||
| return hex.EncodeToString(sum[:]), nil | ||
| } | ||
|
|
||
| func render(dmgDir, repo, tag string) (string, error) { | ||
| byKey := make(map[string]arch, len(arches)) | ||
| for _, a := range arches { | ||
| sum, err := fileSHA256(filepath.Join(dmgDir, a.DMG)) | ||
| if err != nil { | ||
| return "", fmt.Errorf("checksum %s: %w", a.DMG, err) | ||
| } | ||
| a.SHA256 = sum | ||
| byKey[a.Cask] = a | ||
| } | ||
|
|
||
| tmpl, err := template.New("cask").Parse(caskTmpl) | ||
| if err != nil { | ||
| return "", fmt.Errorf("parse template: %w", err) | ||
| } | ||
| var sb strings.Builder | ||
| data := struct { | ||
| Version string | ||
| Repo string | ||
| Arches map[string]arch | ||
| }{Version: strings.TrimPrefix(tag, "v"), Repo: repo, Arches: byKey} | ||
| if err := tmpl.Execute(&sb, data); err != nil { | ||
| return "", fmt.Errorf("render template: %w", err) | ||
| } | ||
| return sb.String(), nil | ||
| } | ||
|
|
||
| func main() { | ||
| dmgDir := flag.String( | ||
| "dmg-dir", | ||
| "", | ||
| "directory containing the Devsy_mac_<arch>.dmg release images", | ||
| ) | ||
| repo := flag.String("repo", "", "owner/repo the release assets are published under") | ||
| tag := flag.String("tag", "", "release tag (e.g. v1.2.3)") | ||
| out := flag.String("out", "", "path to write the generated cask") | ||
| flag.Parse() | ||
|
|
||
| if *dmgDir == "" || *repo == "" || *tag == "" || *out == "" { | ||
| fmt.Fprintln( | ||
| os.Stderr, | ||
| "usage: homebrew_cask --dmg-dir DIR --repo OWNER/REPO --tag TAG --out FILE", | ||
| ) | ||
| os.Exit(2) | ||
| } | ||
|
|
||
| cask, err := render(*dmgDir, *repo, *tag) | ||
| if err != nil { | ||
| fmt.Fprintln(os.Stderr, err) | ||
| os.Exit(1) | ||
| } | ||
| // #nosec G306 -- cask is public. | ||
| if err := os.WriteFile(*out, []byte(cask), 0o644); err != nil { | ||
| fmt.Fprintln(os.Stderr, err) | ||
| os.Exit(1) | ||
| } | ||
| fmt.Printf("wrote %s\n", *out) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "os" | ||
| "path/filepath" | ||
| "strings" | ||
| "testing" | ||
| ) | ||
|
|
||
| func writeDMGs(t *testing.T) string { | ||
| t.Helper() | ||
| dir := t.TempDir() | ||
| for _, a := range arches { | ||
| path := filepath.Join(dir, a.DMG) | ||
| if err := os.WriteFile(path, []byte("content-"+a.DMG), 0o644); err != nil { | ||
| t.Fatalf("write %s: %v", a.DMG, err) | ||
| } | ||
| } | ||
| return dir | ||
| } | ||
|
|
||
| func TestRender(t *testing.T) { | ||
| out, err := render(writeDMGs(t), "devsy-org/devsy", "v1.2.3") | ||
| if err != nil { | ||
| t.Fatalf("render: %v", err) | ||
| } | ||
|
|
||
| // sha256("content-Devsy_mac_arm64.dmg") | ||
| const wantARM = "f65094affc51b44bb65e948773afbdb527e5073f82569bcd9361deb17581807b" | ||
| // sha256("content-Devsy_mac_x64.dmg") | ||
| const wantIntel = "f1045e0e16797365c7f23e330cbab90e74218dc48b55a030fe23a93f4e8c6b9e" | ||
|
|
||
| for _, want := range []string{ | ||
| `version "1.2.3"`, // leading v stripped | ||
| `cask "devsy" do`, | ||
| `arch arm: "arm64", intel: "x64"`, | ||
| "https://github.com/devsy-org/devsy/releases/download/v#{version}/Devsy_mac_#{arch}.dmg", | ||
| `app "Devsy.app"`, | ||
| `homepage "https://www.devsy.sh"`, | ||
| `sha256 arm: "` + wantARM + `"`, | ||
| `intel: "` + wantIntel + `"`, | ||
| } { | ||
| if !strings.Contains(out, want) { | ||
| t.Errorf("cask missing %q\n---\n%s", want, out) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| func TestRenderMissingDMG(t *testing.T) { | ||
| dir := writeDMGs(t) | ||
| if err := os.Remove(filepath.Join(dir, "Devsy_mac_x64.dmg")); err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| if _, err := render(dir, "devsy-org/devsy", "v1.2.3"); err == nil { | ||
| t.Fatal("expected error for missing dmg, got nil") | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: devsy-org/devsy
Length of output: 1139
🏁 Script executed:
Repository: devsy-org/devsy
Length of output: 16906
🏁 Script executed:
Repository: devsy-org/devsy
Length of output: 2775
🌐 Web query:
electron-builder 26.15.3 mac artifactName arch variables mac-arm64 release directory💡 Result:
To configure the artifact file name for macOS builds in electron-builder, you can use the artifactName property within your configuration [1][2]. To include the architecture (such as arm64 or x64) in the file name, use the ${arch} variable in your template [3][4][5]. Configuration Example: { "mac": { "artifactName": "${productName}-${version}-${arch}.${ext}" } } Key Details: 1. Supported Variables: You can use several macros in the artifactName template, including ${arch}, ${productName}, ${version}, ${ext}, and environment variables like ${env.VAR_NAME} [4][5]. 2. Arch Variable Behavior: When using ${arch}, if no architecture is specified for the build, electron-builder may remove the macro and any leading separators (like hyphens or underscores) to avoid double punctuation [4]. If you are building both x64 and arm64, including ${arch} is necessary to prevent files from overwriting each other [6][3]. 3. Target Considerations: While standard for DMG and ZIP targets, some specific targets like pkg have historically required explicit macro handling to ensure the arch correctly reflects in the filename [6]. 4. Release Directory: By default, artifacts are placed in the dist/ directory of your project root, unless otherwise configured via the outDir property. If you encounter issues where the architecture is not being reflected in the filename, ensure you are using a recent version of electron-builder, as earlier versions had limitations regarding dynamic expansion for specific targets like pkg [6][7].
Citations:
Make the generated DMG names match the Cask contract.
The macOS matrix uses electron-builder’s arch values, so
artifactName: Devsy_${os}_${arch}.${ext}can emitDevsy_Darwin_arm64.dmg/Devsy_Darwin_x64.dmg, but the cask consumer expectsDevsy_mac_arm64.dmg/Devsy_mac_x64.dmg. Align the filename used byelectron-builderor updatehack/homebrew_caskso publishing does not fail after builds complete.🤖 Prompt for AI Agents