feat: add FreeBSD release builds (#832)

* feat: add FreeBSD release builds

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>

* chore: allow manual release dispatch

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>

* fix: stabilize release workflow on fork

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>

* fix: clean zig download artifact

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>

* fix: use valid zig target for freebsd arm

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>

* fix: disable freebsd arm release build

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>

* fix: switch freebsd build to pure go

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>

* fix: skip release publishing on forks

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>

* fix: satisfy golangci-lint for release PR

---------

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
Jordan Hubbard
2026-01-01 14:51:51 -04:00
committed by GitHub
parent f3f713d77a
commit aa2ea48bf2
12 changed files with 133 additions and 74 deletions

View File

@@ -504,7 +504,7 @@ func discoverRigDaemons() []rigDaemon {
// Similar to routing.resolveRedirect but simplified for activity use.
func resolveBeadsRedirect(beadsDir string) string {
redirectFile := filepath.Join(beadsDir, "redirect")
data, err := os.ReadFile(redirectFile)
data, err := os.ReadFile(redirectFile) // #nosec G304 - redirects are trusted within beads rig paths
if err != nil {
return beadsDir
}
@@ -729,7 +729,9 @@ func runTownActivityFollow(sinceTime time.Time) {
func closeDaemons(daemons []rigDaemon) {
for _, d := range daemons {
if d.client != nil {
d.client.Close()
if err := d.client.Close(); err != nil {
fmt.Fprintf(os.Stderr, "Warning: failed to close daemon client: %v\n", err)
}
}
}
}

View File

@@ -56,7 +56,7 @@ func DetectPendingMigrations(path string) []PendingMigration {
}
// Check for missing sync-branch config (sync migration)
if needsSyncMigration(beadsDir, path) {
if needsSyncMigration(path) {
pending = append(pending, PendingMigration{
Name: "sync",
Description: "Configure sync branch for multi-clone setup",
@@ -206,7 +206,7 @@ func needsTombstonesMigration(beadsDir string) bool {
}
// needsSyncMigration checks if sync-branch should be configured
func needsSyncMigration(beadsDir, repoPath string) bool {
func needsSyncMigration(repoPath string) bool {
// Check if already configured
if syncbranch.GetFromYAML() != "" {
return false

View File

@@ -782,7 +782,7 @@ func runPrepareCommitMsgHook(args []string) int {
}
// Write back
if err := os.WriteFile(msgFile, []byte(sb.String()), 0644); err != nil { // #nosec G306
if err := os.WriteFile(msgFile, []byte(sb.String()), 0600); err != nil { // Restrict permissions per gosec G306
fmt.Fprintf(os.Stderr, "Warning: could not write commit message: %v\n", err)
}

View File

@@ -23,22 +23,22 @@ import (
// JiraSyncStats tracks statistics for a Jira sync operation.
type JiraSyncStats struct {
Pulled int `json:"pulled"`
Pushed int `json:"pushed"`
Created int `json:"created"`
Updated int `json:"updated"`
Skipped int `json:"skipped"`
Errors int `json:"errors"`
Pulled int `json:"pulled"`
Pushed int `json:"pushed"`
Created int `json:"created"`
Updated int `json:"updated"`
Skipped int `json:"skipped"`
Errors int `json:"errors"`
Conflicts int `json:"conflicts"`
}
// JiraSyncResult represents the result of a Jira sync operation.
type JiraSyncResult struct {
Success bool `json:"success"`
Stats JiraSyncStats `json:"stats"`
LastSync string `json:"last_sync,omitempty"`
Error string `json:"error,omitempty"`
Warnings []string `json:"warnings,omitempty"`
Success bool `json:"success"`
Stats JiraSyncStats `json:"stats"`
LastSync string `json:"last_sync,omitempty"`
Error string `json:"error,omitempty"`
Warnings []string `json:"warnings,omitempty"`
}
var jiraCmd = &cobra.Command{
@@ -288,13 +288,13 @@ var jiraStatusCmd = &cobra.Command{
if jsonOutput {
outputJSON(map[string]interface{}{
"configured": configured,
"jira_url": jiraURL,
"jira_project": jiraProject,
"last_sync": lastSync,
"total_issues": len(allIssues),
"with_jira_ref": withJiraRef,
"pending_push": pendingPush,
"configured": configured,
"jira_url": jiraURL,
"jira_project": jiraProject,
"last_sync": lastSync,
"total_issues": len(allIssues),
"with_jira_ref": withJiraRef,
"pending_push": pendingPush,
})
return
}
@@ -610,9 +610,9 @@ Looked in: %v`, name, locations)
// JiraConflict represents a conflict between local and Jira versions.
type JiraConflict struct {
IssueID string
LocalUpdated time.Time
JiraUpdated time.Time
IssueID string
LocalUpdated time.Time
JiraUpdated time.Time
JiraExternalRef string
}
@@ -854,7 +854,9 @@ func fetchJiraIssueTimestamp(ctx context.Context, jiraKey string) (time.Time, er
if err != nil {
return zero, fmt.Errorf("failed to fetch issue %s: %w", jiraKey, err)
}
defer resp.Body.Close()
defer func() {
_ = resp.Body.Close()
}()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)

View File

@@ -385,12 +385,12 @@ var listCmd = &cobra.Command{
longFormat, _ := cmd.Flags().GetBool("long")
sortBy, _ := cmd.Flags().GetString("sort")
reverse, _ := cmd.Flags().GetBool("reverse")
// Pattern matching flags
titleContains, _ := cmd.Flags().GetString("title-contains")
descContains, _ := cmd.Flags().GetString("desc-contains")
notesContains, _ := cmd.Flags().GetString("notes-contains")
// Date range flags
createdAfter, _ := cmd.Flags().GetString("created-after")
createdBefore, _ := cmd.Flags().GetString("created-before")
@@ -398,12 +398,12 @@ var listCmd = &cobra.Command{
updatedBefore, _ := cmd.Flags().GetString("updated-before")
closedAfter, _ := cmd.Flags().GetString("closed-after")
closedBefore, _ := cmd.Flags().GetString("closed-before")
// Empty/null check flags
emptyDesc, _ := cmd.Flags().GetBool("empty-description")
noAssignee, _ := cmd.Flags().GetBool("no-assignee")
noLabels, _ := cmd.Flags().GetBool("no-labels")
// Priority range flags
priorityMinStr, _ := cmd.Flags().GetString("priority-min")
priorityMaxStr, _ := cmd.Flags().GetString("priority-max")
@@ -509,7 +509,7 @@ var listCmd = &cobra.Command{
filter.IDs = ids
}
}
// Pattern matching
if titleContains != "" {
filter.TitleContains = titleContains
@@ -520,7 +520,7 @@ var listCmd = &cobra.Command{
if notesContains != "" {
filter.NotesContains = notesContains
}
// Date ranges
if createdAfter != "" {
t, err := parseTimeFlag(createdAfter)
@@ -570,7 +570,7 @@ var listCmd = &cobra.Command{
}
filter.ClosedBefore = &t
}
// Empty/null checks
if emptyDesc {
filter.EmptyDescription = true
@@ -581,7 +581,7 @@ var listCmd = &cobra.Command{
if noLabels {
filter.NoLabels = true
}
// Priority ranges
if cmd.Flags().Changed("priority-min") {
priorityMin, err := validation.ValidatePriority(priorityMinStr)
@@ -640,7 +640,7 @@ var listCmd = &cobra.Command{
}
}
// If daemon is running, use RPC
// If daemon is running, use RPC
if daemonClient != nil {
listArgs := &rpc.ListArgs{
Status: status,
@@ -665,17 +665,17 @@ var listCmd = &cobra.Command{
}
// Forward title search via Query field (searches title/description/id)
if titleSearch != "" {
listArgs.Query = titleSearch
listArgs.Query = titleSearch
}
if len(filter.IDs) > 0 {
listArgs.IDs = filter.IDs
}
if len(filter.IDs) > 0 {
listArgs.IDs = filter.IDs
}
// Pattern matching
listArgs.TitleContains = titleContains
listArgs.DescriptionContains = descContains
listArgs.NotesContains = notesContains
// Date ranges
if filter.CreatedAfter != nil {
listArgs.CreatedAfter = filter.CreatedAfter.Format(time.RFC3339)
@@ -695,12 +695,12 @@ var listCmd = &cobra.Command{
if filter.ClosedBefore != nil {
listArgs.ClosedBefore = filter.ClosedBefore.Format(time.RFC3339)
}
// Empty/null checks
listArgs.EmptyDescription = filter.EmptyDescription
listArgs.NoAssignee = filter.NoAssignee
listArgs.NoLabels = filter.NoLabels
// Priority range
listArgs.PriorityMin = filter.PriorityMin
listArgs.PriorityMax = filter.PriorityMax
@@ -721,7 +721,7 @@ var listCmd = &cobra.Command{
}
}
resp, err := daemonClient.List(listArgs)
resp, err := daemonClient.List(listArgs)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
@@ -774,7 +774,9 @@ var listCmd = &cobra.Command{
// Output with pager support
if err := ui.ToPager(buf.String(), ui.PagerOptions{NoPager: noPager}); err != nil {
fmt.Fprint(os.Stdout, buf.String())
if _, writeErr := fmt.Fprint(os.Stdout, buf.String()); writeErr != nil {
fmt.Fprintf(os.Stderr, "Error writing output: %v\n", writeErr)
}
}
// Show truncation hint if we hit the limit (GH#788)
@@ -788,21 +790,21 @@ var listCmd = &cobra.Command{
// ctx already created above for staleness check
issues, err := store.SearchIssues(ctx, "", filter)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
// If no issues found, check if git has issues and auto-import
if len(issues) == 0 {
if checkAndAutoImport(ctx, store) {
// Re-run the query after import
issues, err = store.SearchIssues(ctx, "", filter)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
// If no issues found, check if git has issues and auto-import
if len(issues) == 0 {
if checkAndAutoImport(ctx, store) {
// Re-run the query after import
issues, err = store.SearchIssues(ctx, "", filter)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
}
}
}
// Apply sorting
sortIssues(issues, sortBy, reverse)
@@ -899,7 +901,9 @@ var listCmd = &cobra.Command{
// Output with pager support
if err := ui.ToPager(buf.String(), ui.PagerOptions{NoPager: noPager}); err != nil {
fmt.Fprint(os.Stdout, buf.String())
if _, writeErr := fmt.Fprint(os.Stdout, buf.String()); writeErr != nil {
fmt.Fprintf(os.Stderr, "Error writing output: %v\n", writeErr)
}
}
// Show truncation hint if we hit the limit (GH#788)
@@ -927,12 +931,12 @@ func init() {
listCmd.Flags().Bool("long", false, "Show detailed multi-line output for each issue")
listCmd.Flags().String("sort", "", "Sort by field: priority, created, updated, closed, status, id, title, type, assignee")
listCmd.Flags().BoolP("reverse", "r", false, "Reverse sort order")
// Pattern matching
listCmd.Flags().String("title-contains", "", "Filter by title substring (case-insensitive)")
listCmd.Flags().String("desc-contains", "", "Filter by description substring (case-insensitive)")
listCmd.Flags().String("notes-contains", "", "Filter by notes substring (case-insensitive)")
// Date ranges
listCmd.Flags().String("created-after", "", "Filter issues created after date (YYYY-MM-DD or RFC3339)")
listCmd.Flags().String("created-before", "", "Filter issues created before date (YYYY-MM-DD or RFC3339)")
@@ -940,12 +944,12 @@ func init() {
listCmd.Flags().String("updated-before", "", "Filter issues updated before date (YYYY-MM-DD or RFC3339)")
listCmd.Flags().String("closed-after", "", "Filter issues closed after date (YYYY-MM-DD or RFC3339)")
listCmd.Flags().String("closed-before", "", "Filter issues closed before date (YYYY-MM-DD or RFC3339)")
// Empty/null checks
listCmd.Flags().Bool("empty-description", false, "Filter issues with empty or missing description")
listCmd.Flags().Bool("no-assignee", false, "Filter issues with no assignee")
listCmd.Flags().Bool("no-labels", false, "Filter issues with no labels")
// Priority ranges
listCmd.Flags().String("priority-min", "", "Filter by minimum priority (inclusive, 0-4 or P0-P4)")
listCmd.Flags().String("priority-max", "", "Filter by maximum priority (inclusive, 0-4 or P0-P4)")

View File

@@ -139,7 +139,9 @@ func runChecks(jsonOutput bool) {
}
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
enc.Encode(result)
if err := enc.Encode(result); err != nil {
fmt.Fprintf(os.Stderr, "Error encoding preflight result: %v\n", err)
}
} else {
// Human-readable output
for _, r := range results {