diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 92a6dfe..ee449dc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,74 +13,182 @@ on: Description: "Extra plan args" required: false default: "" + extraCheckArgs: + Description: "Extra check args" + required: false + default: "" extraBuildArgs: Description: "Extra build args" required: false default: "" + extraPackageArgs: + Description: "Extra package args" + required: false + default: "" extraReleaseArgs: Description: "Extra release args" required: false default: "" jobs: - build-and-publish: + plan: runs-on: macos-10.15 + outputs: + check: "${{ steps.check.outcome }}" steps: - - name: Checkout emacs-builds repo - uses: actions/checkout@v2 - with: - path: releaser - name: Checkout build-emacs-for-macos repo uses: actions/checkout@v2 with: repository: jimeh/build-emacs-for-macos - ref: "0.4.16" + ref: "v0.5.0" path: builder - - uses: ruby/setup-ruby@v1 - with: - ruby-version: 2.7 - uses: actions/setup-go@v2 with: go-version: 1.16 - - name: Compile github-release tool - run: >- - go build -o ./github-release ./cmd/github-release - working-directory: releaser + - name: Pre-build emacs-builder tool + run: make build + working-directory: builder - name: Plan build run: >- - ./releaser/github-release --plan plan.yml plan - --work-dir '${{ github.workspace }}' + builder/bin/emacs-builder -l debug plan + --output build-plan.yml + --output-dir '${{ github.workspace }}/builds' ${{ github.event.inputs.extraPlanArgs }} - ${{ github.event.inputs.gitRef }} + '${{ github.event.inputs.gitRef }}' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Show plan - run: >- - cat plan.yml + run: cat build-plan.yml - name: Check if planned release and asset already exist id: check continue-on-error: true run: >- - ./releaser/github-release --plan plan.yml check + builder/bin/emacs-builder -l debug release --plan build-plan.yml check + ${{ github.event.inputs.extraCheckArgs }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Upload pre-built emacs-builder artifact + uses: actions/upload-artifact@v2 + with: + name: emacs-builder + path: builder/bin/emacs-builder + if-no-files-found: error + - name: Upload build-plan.yml artifact + uses: actions/upload-artifact@v2 + with: + name: build-plan.yml + path: build-plan.yml + if-no-files-found: error + + build: + runs-on: macos-10.15 + needs: [plan] + # Only run if check for existing release and asset failed. + if: ${{ needs.plan.outputs.check == 'failure' }} + steps: + - name: Checkout build-emacs-for-macos repo + uses: actions/checkout@v2 + with: + repository: jimeh/build-emacs-for-macos + ref: "v0.5.0" + path: builder + - uses: ruby/setup-ruby@v1 + with: + ruby-version: 2.7 - name: Install dependencies - if: steps.check.outcome == 'failure' - run: >- - brew bundle --file=builder/Brewfile + run: make bootstrap-ci + working-directory: builder + - name: Download pre-built emacs-builder artifact + uses: actions/download-artifact@v2 + id: builder + with: + name: emacs-builder + path: bin + - name: Ensure emacs-builder is executable + run: chmod +x bin/emacs-builder + - name: Download build-plan.yml artifact + uses: actions/download-artifact@v2 + id: plan + with: + name: build-plan.yml + path: ./ - name: Build Emacs - if: steps.check.outcome == 'failure' run: >- - ./builder/build-emacs-for-macos --plan=plan.yml - --work-dir '${{ github.workspace }}' - --native-full-aot - ${{ github.event.inputs.extraReleaseArgs }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Publish release - if: steps.check.outcome == 'failure' - run: >- - ./releaser/github-release --plan plan.yml publish + ./builder/build-emacs-for-macos --plan build-plan.yml + --native-full-aot ${{ github.event.inputs.extraBuildArgs }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Install the Apple signing certificate + run: | + # create variables + CERTIFICATE_PATH="$RUNNER_TEMP/build_certificate.p12" + KEYCHAIN_PATH="$RUNNER_TEMP/app-signing.keychain-db" + + # import certificate and provisioning profile from secrets + echo -n "$CERT_BASE64" | base64 --decode --output "$CERTIFICATE_PATH" + + # create temporary keychain + security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" + security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH" + security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" + + # import certificate to keychain + security import "$CERTIFICATE_PATH" -P "$CERT_PASSWORD" -A \ + -t cert -f pkcs12 -k "$KEYCHAIN_PATH" + security list-keychain -d user -s "$KEYCHAIN_PATH" + env: + CERT_BASE64: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_P12_BASE64 }} + CERT_PASSWORD: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_PASSWORD }} + KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} + - name: Sign, package and notarize build + run: >- + bin/emacs-builder -l debug package -v --plan build-plan.yml + --sign --remove-source-dir + ${{ github.event.inputs.extraPackageArgs }} + env: + AC_USERNAME: ${{ secrets.AC_USERNAME }} + AC_PASSWORD: ${{ secrets.AC_PASSWORD }} + AC_PROVIDER: ${{ secrets.AC_PROVIDER }} + AC_SIGN_IDENTITY: ${{ secrets.AC_SIGN_IDENTITY }} + - name: Upload disk image artifacts + uses: actions/upload-artifact@v2 + with: + name: dmg + path: | + builds/*.dmg + builds/*.sha* + if-no-files-found: error + - name: Clean up keychain used for signing certificate + if: ${{ always() }} + run: | + security delete-keychain "$RUNNER_TEMP/app-signing.keychain-db" + + release: + runs-on: macos-10.15 + needs: [build] + steps: + - name: Download pre-built emacs-builder artifact + uses: actions/download-artifact@v2 + id: builder + with: + name: emacs-builder + path: bin + - name: Ensure emacs-builder is executable + run: chmod +x bin/emacs-builder + - name: Download build-plan.yml artifact + uses: actions/download-artifact@v2 + id: plan + with: + name: build-plan.yml + path: ./ + - name: Download disk image artifact + uses: actions/download-artifact@v2 + with: + name: dmg + path: builds + - name: Publish disk image to GitHub Release + run: >- + bin/emacs-builder -l debug release --plan build-plan.yml publish ${{ github.event.inputs.extraReleaseArgs }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml index 81dda16..4a0a28a 100644 --- a/.github/workflows/test-build.yml +++ b/.github/workflows/test-build.yml @@ -7,18 +7,13 @@ on: description: "Emacs git ref to build" required: true default: "master" - releaseToolGitRef: - description: "Git ref to checkout of emacs-builds (github-release)" - required: true - default: "main" - buildScriptGitRef: + emacsBuilderGitRef: description: "Git ref to checkout of build-emacs-for-macos" required: true default: "master" testBuildName: description: "Test build name" - required: false - default: "" + required: true testReleaseType: description: "prerelease or draft" required: true @@ -27,77 +22,184 @@ on: Description: "Extra plan args" required: false default: "" + extraCheckArgs: + Description: "Extra check args" + required: false + default: "" extraBuildArgs: Description: "Extra build args" required: false default: "" + extraPackageArgs: + Description: "Extra package args" + required: false + default: "" extraReleaseArgs: Description: "Extra release args" required: false default: "" jobs: - build-and-publish: + plan: runs-on: macos-10.15 + outputs: + check: "${{ steps.check.outcome }}" steps: - - name: Checkout emacs-builds repo - uses: actions/checkout@v2 - with: - ref: ${{ github.event.inputs.releaseToolGitRef }} - path: releaser - name: Checkout build-emacs-for-macos repo uses: actions/checkout@v2 with: repository: jimeh/build-emacs-for-macos - ref: ${{ github.event.inputs.buildScriptGitRef }} + ref: ${{ github.event.inputs.emacsBuilderGitRef }} path: builder - - uses: ruby/setup-ruby@v1 - with: - ruby-version: 2.7 - uses: actions/setup-go@v2 with: go-version: 1.16 - - name: Compile github-release tool - run: >- - go build -o ./github-release ./cmd/github-release - working-directory: releaser + - name: Pre-build emacs-builder tool + run: make build + working-directory: builder - name: Plan build run: >- - ./releaser/github-release --plan plan.yml plan - --work-dir '${{ github.workspace }}' - --test-build - --test-build-name="${{ github.event.inputs.testBuildName }}" - --test-release-type="${{ github.event.inputs.testReleaseType }}" + builder/bin/emacs-builder -l debug plan + --output build-plan.yml + --output-dir '${{ github.workspace }}/builds' + --test-build '${{ github.event.inputs.testBuildName }}' + --test-release-type '${{ github.event.inputs.testReleaseType }}' ${{ github.event.inputs.extraPlanArgs }} - ${{ github.event.inputs.gitRef }} + '${{ github.event.inputs.gitRef }}' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Show plan - run: >- - cat plan.yml + run: cat build-plan.yml - name: Check if planned release and asset already exist id: check continue-on-error: true run: >- - ./releaser/github-release --plan plan.yml check + builder/bin/emacs-builder -l debug release --plan build-plan.yml check + ${{ github.event.inputs.extraCheckArgs }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Upload pre-built emacs-builder artifact + uses: actions/upload-artifact@v2 + with: + name: emacs-builder + path: builder/bin/emacs-builder + if-no-files-found: error + - name: Upload build-plan.yml artifact + uses: actions/upload-artifact@v2 + with: + name: build-plan.yml + path: build-plan.yml + if-no-files-found: error + + build: + runs-on: macos-10.15 + needs: [plan] + # Only run if check for existing release and asset failed. + if: ${{ needs.plan.outputs.check == 'failure' }} + steps: + - name: Checkout build-emacs-for-macos repo + uses: actions/checkout@v2 + with: + repository: jimeh/build-emacs-for-macos + ref: ${{ github.event.inputs.emacsBuilderGitRef }} + path: builder + - uses: ruby/setup-ruby@v1 + with: + ruby-version: 2.7 - name: Install dependencies - if: steps.check.outcome == 'failure' - run: >- - brew bundle --file=builder/Brewfile + run: make bootstrap-ci + working-directory: builder + - name: Download pre-built emacs-builder artifact + uses: actions/download-artifact@v2 + id: builder + with: + name: emacs-builder + path: bin + - name: Ensure emacs-builder is executable + run: chmod +x bin/emacs-builder + - name: Download build-plan.yml artifact + uses: actions/download-artifact@v2 + id: plan + with: + name: build-plan.yml + path: ./ - name: Build Emacs - if: steps.check.outcome == 'failure' run: >- - ./builder/build-emacs-for-macos --plan=plan.yml - --work-dir '${{ github.workspace }}' - --native-full-aot - ${{ github.event.inputs.extraReleaseArgs }} - - name: Publish release - if: steps.check.outcome == 'failure' + ./builder/build-emacs-for-macos --plan build-plan.yml + --native-full-aot ${{ github.event.inputs.extraBuildArgs }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Install the Apple signing certificate + run: | + # create variables + CERTIFICATE_PATH="$RUNNER_TEMP/build_certificate.p12" + KEYCHAIN_PATH="$RUNNER_TEMP/app-signing.keychain-db" + + # import certificate and provisioning profile from secrets + echo -n "$CERT_BASE64" | base64 --decode --output "$CERTIFICATE_PATH" + + # create temporary keychain + security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" + security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH" + security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" + + # import certificate to keychain + security import "$CERTIFICATE_PATH" -P "$CERT_PASSWORD" -A \ + -t cert -f pkcs12 -k "$KEYCHAIN_PATH" + security list-keychain -d user -s "$KEYCHAIN_PATH" + env: + CERT_BASE64: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_P12_BASE64 }} + CERT_PASSWORD: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_PASSWORD }} + KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} + - name: Sign, package and notarize build run: >- - ./releaser/github-release --plan plan.yml publish - --release-sha="${{ github.event.inputs.releaseToolGitRef }}" + bin/emacs-builder -l debug package -v --plan build-plan.yml + --sign --remove-source-dir + ${{ github.event.inputs.extraPackageArgs }} + env: + AC_USERNAME: ${{ secrets.AC_USERNAME }} + AC_PASSWORD: ${{ secrets.AC_PASSWORD }} + AC_PROVIDER: ${{ secrets.AC_PROVIDER }} + AC_SIGN_IDENTITY: ${{ secrets.AC_SIGN_IDENTITY }} + - name: Upload disk image artifacts + uses: actions/upload-artifact@v2 + with: + name: dmg + path: | + builds/*.dmg + builds/*.sha* + if-no-files-found: error + - name: Clean up keychain used for signing certificate + if: ${{ always() }} + run: | + security delete-keychain "$RUNNER_TEMP/app-signing.keychain-db" + + release: + runs-on: macos-10.15 + needs: [build] + steps: + - name: Download pre-built emacs-builder artifact + uses: actions/download-artifact@v2 + id: builder + with: + name: emacs-builder + path: bin + - name: Ensure emacs-builder is executable + run: chmod +x bin/emacs-builder + - name: Download build-plan.yml artifact + uses: actions/download-artifact@v2 + id: plan + with: + name: build-plan.yml + path: ./ + - name: Download disk image artifact + uses: actions/download-artifact@v2 + with: + name: dmg + path: builds + - name: Publish disk image to GitHub Release + run: >- + bin/emacs-builder -l debug release --plan build-plan.yml publish ${{ github.event.inputs.extraReleaseArgs }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/cmd/github-release/check_cmd.go b/cmd/github-release/check_cmd.go deleted file mode 100644 index 8e50530..0000000 --- a/cmd/github-release/check_cmd.go +++ /dev/null @@ -1,57 +0,0 @@ -package main - -import ( - "fmt" - "net/http" - "path/filepath" - - "github.com/urfave/cli/v2" -) - -func checkCmd() *cli.Command { - return &cli.Command{ - Name: "check", - Usage: "Check if GitHub release and asset exists", - UsageText: "github-release [global options] check [options]", - Action: actionHandler(checkAction), - } -} - -func checkAction(c *cli.Context, opts *globalOptions) error { - gh := opts.gh - repo := opts.repo - plan, err := LoadPlan(opts.plan) - if err != nil { - return err - } - - fmt.Printf( - "==> Checking github.com/%s for release: %s\n", - repo.String(), plan.Release.Name, - ) - - release, resp, err := gh.Repositories.GetReleaseByTag( - c.Context, repo.Owner, repo.Name, plan.Release.Name, - ) - if err != nil { - if resp.StatusCode == http.StatusNotFound { - return fmt.Errorf("release %s does not exist", plan.Release.Name) - } else { - return err - } - } - - fmt.Println(" -> Release exists") - - filename := filepath.Base(plan.Archive) - - fmt.Printf("==> Checking release for asset: %s\n", filename) - for _, a := range release.Assets { - if a.Name != nil && filename == *a.Name { - fmt.Println(" -> Asset exists") - return nil - } - } - - return fmt.Errorf("release does contain asset: %s", filename) -} diff --git a/cmd/github-release/commit.go b/cmd/github-release/commit.go deleted file mode 100644 index ae8b9cc..0000000 --- a/cmd/github-release/commit.go +++ /dev/null @@ -1,46 +0,0 @@ -package main - -import ( - "fmt" - "time" - - "github.com/google/go-github/v35/github" -) - -type Commit struct { - Repo *Repo `yaml:"repo"` - Ref string `yaml:"ref"` - SHA string `yaml:"sha"` - Date *time.Time `yaml:"date"` - Author string `yaml:"author"` - Committer string `yaml:"committer"` - Message string `yaml:"message"` -} - -func NewCommit(repo *Repo, ref string, rc *github.RepositoryCommit) *Commit { - return &Commit{ - Repo: repo, - Ref: ref, - SHA: rc.GetSHA(), - Date: rc.GetCommit().GetCommitter().Date, - Author: fmt.Sprintf( - "%s <%s>", - rc.GetCommit().GetAuthor().GetName(), - rc.GetCommit().GetAuthor().GetEmail(), - ), - Committer: fmt.Sprintf( - "%s <%s>", - rc.GetCommit().GetCommitter().GetName(), - rc.GetCommit().GetCommitter().GetEmail(), - ), - Message: rc.GetCommit().GetMessage(), - } -} - -func (s *Commit) ShortSHA() string { - return s.SHA[0:7] -} - -func (s *Commit) DateString() string { - return s.Date.Format("2006-01-02") -} diff --git a/cmd/github-release/github.go b/cmd/github-release/github.go deleted file mode 100644 index c4136be..0000000 --- a/cmd/github-release/github.go +++ /dev/null @@ -1,22 +0,0 @@ -package main - -import ( - "context" - "net/http" - - "github.com/google/go-github/v35/github" - "golang.org/x/oauth2" -) - -func NewGitHubClient(ctx context.Context, token string) *github.Client { - var tc *http.Client - - if token != "" { - ts := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: token}, - ) - tc = oauth2.NewClient(ctx, ts) - } - - return github.NewClient(tc) -} diff --git a/cmd/github-release/main.go b/cmd/github-release/main.go deleted file mode 100644 index 14c0da0..0000000 --- a/cmd/github-release/main.go +++ /dev/null @@ -1,74 +0,0 @@ -package main - -import ( - "fmt" - "os" - - "github.com/google/go-github/v35/github" - "github.com/urfave/cli/v2" -) - -var app = &cli.App{ - Name: "github-release", - Usage: "manage GitHub releases", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "github-token", - Aliases: []string{"t"}, - Usage: "GitHub API Token (read from GITHUB_TOKEN if set)", - }, - &cli.StringFlag{ - Name: "release-repo", - Aliases: []string{"r"}, - Usage: "Owner/name of GitHub repo to publish releases to", - EnvVars: []string{"GITHUB_REPOSITORY"}, - Value: "jimeh/emacs-builds", - }, - &cli.PathFlag{ - Name: "plan", - Aliases: []string{"p"}, - Usage: "Load plan from `FILE`", - EnvVars: []string{"BUILD_PLAN"}, - Required: true, - TakesFile: true, - }, - }, - Commands: []*cli.Command{ - checkCmd(), - publishCmd(), - planCmd(), - }, -} - -type globalOptions struct { - gh *github.Client - repo *Repo - plan string -} - -func actionHandler( - f func(*cli.Context, *globalOptions) error, -) func(*cli.Context) error { - return func(c *cli.Context) error { - token := c.String("github-token") - if t := os.Getenv("GITHUB_TOKEN"); t != "" { - token = t - } - - opts := &globalOptions{ - gh: NewGitHubClient(c.Context, token), - repo: NewRepo(c.String("release-repo")), - plan: c.String("plan"), - } - - return f(c, opts) - } -} - -func main() { - err := app.Run(os.Args) - if err != nil { - fmt.Fprintf(os.Stderr, "ERROR: %s\n", err.Error()) - os.Exit(1) - } -} diff --git a/cmd/github-release/os_info.go b/cmd/github-release/os_info.go deleted file mode 100644 index e7f8d5c..0000000 --- a/cmd/github-release/os_info.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import ( - "os/exec" - "strings" -) - -type OSInfo struct { - Name string `yaml:"name"` - Version string `yaml:"version"` - Arch string `yaml:"arch"` -} - -func NewOSInfo() (*OSInfo, error) { - version, err := exec.Command("sw_vers", "-productVersion").CombinedOutput() - if err != nil { - return nil, err - } - - arch, err := exec.Command("uname", "-m").CombinedOutput() - if err != nil { - return nil, err - } - - return &OSInfo{ - Name: "macOS", - Version: strings.TrimSpace(string(version)), - Arch: strings.TrimSpace(string(arch)), - }, nil -} - -func (s *OSInfo) ShortVersion() string { - parts := strings.Split(s.Version, ".") - max := len(parts) - if max > 2 { - max = 2 - } - - return strings.Join(parts[0:max], ".") -} diff --git a/cmd/github-release/plan.go b/cmd/github-release/plan.go deleted file mode 100644 index 56acb31..0000000 --- a/cmd/github-release/plan.go +++ /dev/null @@ -1,33 +0,0 @@ -package main - -import ( - "os" - - "gopkg.in/yaml.v3" -) - -type Release struct { - Name string `yaml:"name"` - Title string `yaml:"title,omitempty"` - Draft bool `yaml:"draft,omitempty"` - Pre bool `yaml:"prerelease,omitempty"` -} - -type Plan struct { - Commit *Commit `yaml:"commit"` - OS *OSInfo `yaml:"os"` - Release *Release `yaml:"release"` - Archive string `yaml:"archive"` -} - -func LoadPlan(filename string) (*Plan, error) { - b, err := os.ReadFile(filename) - if err != nil { - return nil, err - } - - plan := &Plan{} - err = yaml.Unmarshal(b, plan) - - return plan, err -} diff --git a/cmd/github-release/plan_cmd.go b/cmd/github-release/plan_cmd.go deleted file mode 100644 index fa36434..0000000 --- a/cmd/github-release/plan_cmd.go +++ /dev/null @@ -1,142 +0,0 @@ -package main - -import ( - "bytes" - "fmt" - "os" - "path/filepath" - "regexp" - - "github.com/urfave/cli/v2" - "gopkg.in/yaml.v3" -) - -var nonAlphaNum = regexp.MustCompile(`[^\w-_]+`) - -func planCmd() *cli.Command { - wd, err := os.Getwd() - if err != nil { - wd = "" - } - - return &cli.Command{ - Name: "plan", - Usage: "Plan if GitHub release and asset exists", - UsageText: "github-release [global options] plan []", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "emacs-mirror-repo", - Usage: "Github owner/repo to get Emacs commit info from", - Aliases: []string{"e"}, - EnvVars: []string{"EMACS_MIRROR_REPO"}, - Value: "emacs-mirror/emacs", - }, - &cli.StringFlag{ - Name: "work-dir", - Usage: "Github owner/repo to get Emacs commit info from", - Value: wd, - }, - &cli.StringFlag{ - Name: "sha", - Usage: "Override commit SHA of specified git branch/tag", - }, - &cli.BoolFlag{ - Name: "test-build", - Usage: "Plan a test build, which is published to a draft " + - "\"test-builds\" release", - }, - &cli.StringFlag{ - Name: "test-build-name", - Usage: "Set a unique name to distinguish the ", - }, - &cli.StringFlag{ - Name: "test-release-type", - Value: "prerelease", - Usage: "Type of release when doing a test-build " + - "(prerelease or draft)", - }, - }, - Action: actionHandler(planAction), - } -} - -func planAction(c *cli.Context, opts *globalOptions) error { - gh := opts.gh - planFile := opts.plan - repo := NewRepo(c.String("emacs-mirror-repo")) - buildsDir := filepath.Join(c.String("work-dir"), "builds") - - ref := c.Args().Get(0) - if ref == "" { - ref = "master" - } - - lookupRef := ref - if s := c.String("sha"); s != "" { - lookupRef = s - } - - repoCommit, _, err := gh.Repositories.GetCommit( - c.Context, repo.Owner, repo.Name, lookupRef, - ) - if err != nil { - return err - } - - commit := NewCommit(repo, ref, repoCommit) - osInfo, err := NewOSInfo() - if err != nil { - return err - } - - cleanRef := sanitizeString(ref) - cleanOS := sanitizeString(osInfo.Name + "-" + osInfo.ShortVersion()) - cleanArch := sanitizeString(osInfo.Arch) - - release := &Release{ - Name: fmt.Sprintf( - "Emacs.%s.%s.%s", - commit.DateString(), commit.ShortSHA(), cleanRef, - ), - } - archiveName := fmt.Sprintf( - "Emacs.%s.%s.%s.%s.%s", - commit.DateString(), commit.ShortSHA(), cleanRef, cleanOS, cleanArch, - ) - - if c.Bool("test-build") { - release.Title = "Test Builds" - release.Name = "test-builds" - if c.String("test-release-type") == "draft" { - release.Draft = true - } else { - release.Pre = true - } - - archiveName += ".test" - if t := c.String("test-build-name"); t != "" { - archiveName += "." + sanitizeString(t) - } - } - - plan := &Plan{ - Commit: commit, - OS: osInfo, - Release: release, - Archive: filepath.Join(buildsDir, archiveName+".tbz"), - } - - buf := bytes.Buffer{} - enc := yaml.NewEncoder(&buf) - enc.SetIndent(2) - err = enc.Encode(plan) - if err != nil { - return err - } - - return os.WriteFile(planFile, buf.Bytes(), 0666) -} - -func sanitizeString(s string) string { - return nonAlphaNum.ReplaceAllString(s, "-") -} diff --git a/cmd/github-release/publish_cmd.go b/cmd/github-release/publish_cmd.go deleted file mode 100644 index f25b74f..0000000 --- a/cmd/github-release/publish_cmd.go +++ /dev/null @@ -1,196 +0,0 @@ -package main - -import ( - "crypto/sha256" - "fmt" - "io" - "net/http" - "os" - "path/filepath" - - "github.com/google/go-github/v35/github" - "github.com/urfave/cli/v2" -) - -func publishCmd() *cli.Command { - return &cli.Command{ - Name: "publish", - Usage: "publish a release", - UsageText: "github-release [global-options] publish [options]", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "release-sha", - Aliases: []string{"s"}, - Usage: "Git SHA of repo to create release on", - EnvVars: []string{"GITHUB_SHA"}, - }, - }, - Action: actionHandler(publishAction), - } -} - -func publishAction(c *cli.Context, opts *globalOptions) error { - gh := opts.gh - repo := opts.repo - plan, err := LoadPlan(opts.plan) - if err != nil { - return err - } - - releaseSHA := c.String("release-sha") - - assetBaseName := filepath.Base(plan.Archive) - assetSumFile := plan.Archive + ".sha256" - - if _, err := os.Stat(assetSumFile); os.IsNotExist(err) { - fmt.Printf("==> Generating SHA256 sum for %s\n", assetBaseName) - assetSum, err := fileSHA256(plan.Archive) - if err != nil { - return err - } - - content := fmt.Sprintf("%s %s", assetSum, assetBaseName) - err = os.WriteFile(assetSumFile, []byte(content), 0666) - if err != nil { - return err - } - - fmt.Printf(" -> Done: %s\n", assetSum) - } - - tagName := plan.Release.Name - name := plan.Release.Title - - if name == "" { - name = tagName - } - - fmt.Printf("==> Checking release %s\n", tagName) - - release, resp, err := gh.Repositories.GetReleaseByTag( - c.Context, repo.Owner, repo.Name, plan.Release.Name, - ) - if err != nil { - if resp.StatusCode == http.StatusNotFound { - fmt.Println(" -> Release not found, creating...") - - prerelease := false - if !plan.Release.Draft && plan.Release.Pre { - prerelease = true - } - - release, _, err = gh.Repositories.CreateRelease( - c.Context, repo.Owner, repo.Name, &github.RepositoryRelease{ - Name: &name, - TagName: &tagName, - TargetCommitish: &releaseSHA, - Prerelease: &prerelease, - Draft: &plan.Release.Draft, - }, - ) - if err != nil { - return err - } - } else { - return err - } - } - - if release.GetName() != name { - release.Name = &name - - release, _, err = gh.Repositories.EditRelease( - c.Context, repo.Owner, repo.Name, release.GetID(), release, - ) - if err != nil { - return err - } - } - - if !plan.Release.Draft && release.GetPrerelease() != plan.Release.Pre { - release.Prerelease = &plan.Release.Pre - - release, _, err = gh.Repositories.EditRelease( - c.Context, repo.Owner, repo.Name, release.GetID(), release, - ) - if err != nil { - return err - } - } - - assetFiles := []string{plan.Archive, assetSumFile} - - for _, fileName := range assetFiles { - fileIO, err := os.Open(fileName) - if err != nil { - return err - } - defer fileIO.Close() - - fileInfo, err := fileIO.Stat() - if err != nil { - return err - } - - fileBaseName := filepath.Base(fileName) - assetExists := false - - fmt.Printf("==> Checking asset %s\n", fileBaseName) - - for _, a := range release.Assets { - if a.GetName() != fileBaseName { - continue - } - - if a.GetSize() == int(fileInfo.Size()) { - fmt.Println(" -> Asset already exists") - assetExists = true - } else { - fmt.Println( - " -> Asset exists with wrong file size, deleting...", - ) - _, err := gh.Repositories.DeleteReleaseAsset( - c.Context, repo.Owner, repo.Name, a.GetID(), - ) - if err != nil { - return err - } - fmt.Println(" -> Done") - } - - } - - if !assetExists { - fmt.Println(" -> Asset missing, uploading...") - _, _, err = gh.Repositories.UploadReleaseAsset( - c.Context, repo.Owner, repo.Name, release.GetID(), - &github.UploadOptions{Name: fileBaseName}, - fileIO, - ) - if err != nil { - return err - } - fmt.Println(" -> Done") - } - - } - - fmt.Printf("==> Release available at: %s\n", release.GetHTMLURL()) - - return nil -} - -func fileSHA256(filename string) (string, error) { - f, err := os.Open(filename) - if err != nil { - return "", err - } - defer f.Close() - - h := sha256.New() - if _, err := io.Copy(h, f); err != nil { - return "", err - } - - return fmt.Sprintf("%x", h.Sum(nil)), nil -} diff --git a/cmd/github-release/repo.go b/cmd/github-release/repo.go deleted file mode 100644 index f7c00da..0000000 --- a/cmd/github-release/repo.go +++ /dev/null @@ -1,26 +0,0 @@ -package main - -import ( - "fmt" - "strings" -) - -type Repo struct { - URL string `yaml:"url"` - Owner string `yaml:"owner"` - Name string `yaml:"name"` -} - -func NewRepo(ownerAndRepo string) *Repo { - parts := strings.SplitN(ownerAndRepo, "/", 2) - - return &Repo{ - URL: fmt.Sprintf("https://github.com/%s/%s", parts[0], parts[1]), - Owner: parts[0], - Name: parts[1], - } -} - -func (s *Repo) String() string { - return s.Owner + "/" + s.Name -}