diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a822c1770..f0eeca3c1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -3,7 +3,7 @@ name: Main Workflow on: pull_request: {} push: - branches: [ "main" ] + branches: [ "main", "beta" ] concurrency: @@ -65,7 +65,9 @@ jobs: needs: unit-tests # Skip running if the PR is coming from a fork or is created by dependabot or snyk due to missing repo secrets. - if: github.event.pull_request.head.repo.fork == false && (github.actor != 'dependabot[bot]' && github.actor != 'snyk-bot') + # Only run on pushes to main or PRs targeting main. + if: github.event.pull_request.head.repo.fork == false && (github.actor != 'dependabot[bot]' && github.actor != 'snyk-bot') && + (github.ref == 'refs/heads/main' || github.base_ref == 'main') steps: - name: Check out the code diff --git a/.github/workflows/release-beta.yml b/.github/workflows/release-beta.yml new file mode 100644 index 000000000..42e489136 --- /dev/null +++ b/.github/workflows/release-beta.yml @@ -0,0 +1,39 @@ +name: Release Beta + +on: + push: + tags: + # Prerelease tags only, e.g. v1.2.3-beta.1. Stable releases such as + # v1.2.3 are handled by release.yml. + - "v[0-9]+.[0-9]+.[0-9]+-*" + +permissions: + contents: write + +jobs: + goreleaser: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + with: + fetch-depth: 0 # This ensures all history and tags are fetched + path: auth0-cli + + - name: Set up Go + uses: actions/setup-go@be3c94b385c4f180051c996d336f57a34c397495 # v3.6.1 + with: + go-version-file: auth0-cli/go.mod + check-latest: true + + # GoReleaser automatically marks SemVer prerelease tags (those with a + # "-suffix") as GitHub prereleases. Homebrew and Scoop PRs are + # intentionally omitted so beta builds never reach package managers. + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@90a3faa9d0182683851fbfa97ca1a2cb983bfca3 # pin@6.2.1 + with: + version: "2.7.0" + args: release --clean + workdir: 'auth0-cli' + env: + GITHUB_TOKEN: ${{ github.token }} \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ff329dc07..dedc40443 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,7 +3,10 @@ name: Release on: push: tags: - - "v*" + # Stable releases only, e.g. v1.2.3. Prerelease tags such as + # v1.2.3-beta.1 are handled by release-beta.yml. + - "v[0-9]+.[0-9]+.[0-9]+" + permissions: contents: write @@ -32,7 +35,6 @@ jobs: workdir: 'auth0-cli' env: GITHUB_TOKEN: ${{ github.token }} - SENTRY_DSN: ${{ secrets.SENTRY_DSN }} # Homebrew Tap Process - name: Checkout Homebrew Tap Repo diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 44a0d7fd2..19f8f7ad9 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -3,7 +3,7 @@ name: Security on: pull_request: {} push: - branches: [ "main" ] + branches: [ "main", "beta" ] schedule: - cron: "30 0 1,15 * *" diff --git a/.goreleaser.yml b/.goreleaser.yml index 8540cbbd6..6e7dad18c 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -17,7 +17,6 @@ builds: - -X 'github.com/auth0/auth0-cli/internal/buildinfo.Revision={{.Commit}}' - -X 'github.com/auth0/auth0-cli/internal/buildinfo.BuildUser=goreleaser' - -X 'github.com/auth0/auth0-cli/internal/buildinfo.BuildDate={{.Date}}' - - -X 'github.com/auth0/auth0-cli/internal/instrumentation.SentryDSN={{.Env.SENTRY_DSN}}' archives: - name_template: '{{ .ProjectName }}_{{ .Version }}_{{ title .Os }}_{{ if eq .Arch "arm64" }}arm64{{ else }}x86_64{{ end }}' files: diff --git a/go.mod b/go.mod index 962b56fde..d7d2da3a1 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/PuerkitoBio/rehttp v1.4.0 github.com/atotto/clipboard v0.1.4 github.com/auth0/go-auth0 v1.43.0 - github.com/auth0/go-auth0/v2 v2.13.0 + github.com/auth0/go-auth0/v2 v2.14.0 github.com/briandowns/spinner v1.23.2 github.com/charmbracelet/glamour v1.0.0 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e diff --git a/go.sum b/go.sum index b6c15577e..ceed561bb 100644 --- a/go.sum +++ b/go.sum @@ -22,8 +22,8 @@ github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/auth0/go-auth0 v1.43.0 h1:sbtJHqukY1esWqlvyRpwIZ/If1m4h8e24TW2UztMXtA= github.com/auth0/go-auth0 v1.43.0/go.mod h1:32sQB1uAn+99fJo6N819EniKq8h785p0ag0lMWhiTaE= -github.com/auth0/go-auth0/v2 v2.13.0 h1:Lf1cPRypkb879mHin1GlGS6NtWkO47Efo0Bq2HIMez4= -github.com/auth0/go-auth0/v2 v2.13.0/go.mod h1:Q/Y3VZVoI3sw87VyTPhx2TQL6Sq4Q/iCP67rW2gcn+M= +github.com/auth0/go-auth0/v2 v2.14.0 h1:zDxwRHGAt6gLK/OG6wAkB5ScQEJ8WW/ex1EnJig8fFc= +github.com/auth0/go-auth0/v2 v2.14.0/go.mod h1:Q/Y3VZVoI3sw87VyTPhx2TQL6Sq4Q/iCP67rW2gcn+M= github.com/aybabtme/iocontrol v0.0.0-20150809002002-ad15bcfc95a0 h1:0NmehRCgyk5rljDQLKUO+cRJCnduDyn11+zGZIc9Z48= github.com/aybabtme/iocontrol v0.0.0-20150809002002-ad15bcfc95a0/go.mod h1:6L7zgvqo0idzI7IO8de6ZC051AfXb5ipkIJ7bIA2tGA= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= diff --git a/internal/instrumentation/instrumentation.go b/internal/instrumentation/instrumentation.go index 385ff91a4..dadec5e49 100644 --- a/internal/instrumentation/instrumentation.go +++ b/internal/instrumentation/instrumentation.go @@ -5,9 +5,17 @@ import ( "time" "github.com/getsentry/sentry-go" + + "github.com/auth0/auth0-cli/internal/buildinfo" ) -var SentryDSN string +// SentryDSN is the destination for crash reports. A Sentry DSN is a public, +// write-only key that is safe to ship inside client binaries, so we hardcode a +// default here. This ensures crash reporting works for builds that are not +// produced by our release pipeline (for example Homebrew Core, which builds +// from source and cannot inject build-time values). Release builds may still +// override this via ldflags. +var SentryDSN = "https://370df87d33df46cb90182dd80a50fdc4@o27592.ingest.sentry.io/5694458" // ReportException is designed to be called once as the CLI exits. We're // purposefully initializing a client all the time given this context. @@ -16,6 +24,15 @@ func ReportException(err error) bool { return false } + // Skip crash reporting for local/development builds so that dev-time panics + // and errors are not shipped to Sentry. Release pipelines (goreleaser and + // Homebrew Core) stamp a real semantic version via ldflags, whereas a local + // `make build`/`make install` stamps "dev" and a plain `go build` leaves it + // empty. + if buildinfo.Version == "" || buildinfo.Version == "dev" { + return false + } + if err := sentry.Init(sentry.ClientOptions{Dsn: SentryDSN}); err != nil { return false } diff --git a/internal/instrumentation/instrumentation_test.go b/internal/instrumentation/instrumentation_test.go new file mode 100644 index 000000000..044f0ebc3 --- /dev/null +++ b/internal/instrumentation/instrumentation_test.go @@ -0,0 +1,62 @@ +package instrumentation + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/auth0/auth0-cli/internal/buildinfo" +) + +func TestReportException(t *testing.T) { + tests := []struct { + name string + sentryDSN string + version string + want bool + }{ + { + name: "skips when Sentry DSN is empty", + sentryDSN: "", + version: "1.32.0", + want: false, + }, + { + name: "skips for a plain go build with no version", + sentryDSN: "https://public@o0.ingest.sentry.io/0", + version: "", + want: false, + }, + { + name: "skips for a local dev build", + sentryDSN: "https://public@o0.ingest.sentry.io/0", + version: "dev", + want: false, + }, + { + name: "reports for a real release build", + sentryDSN: "https://public@o0.ingest.sentry.io/0", + version: "1.32.0", + want: true, + }, + } + + originalDSN := SentryDSN + originalVersion := buildinfo.Version + t.Cleanup(func() { + SentryDSN = originalDSN + buildinfo.Version = originalVersion + }) + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + SentryDSN = test.sentryDSN + buildinfo.Version = test.version + + got := ReportException(errors.New("boom")) + + assert.Equal(t, test.want, got) + }) + } +}