├── docs
├── favicon.ico
├── htmltest.yml
├── assets
│ └── images
│ │ ├── icon.png
│ │ ├── banner.png
│ │ ├── header.png
│ │ ├── favicon_120px.png
│ │ ├── favicon_152px.png
│ │ ├── favicon_167px.png
│ │ ├── favicon_16px.png
│ │ ├── favicon_180px.png
│ │ ├── favicon_32px.png
│ │ └── favicon_96px.png
├── Gemfile
├── commands
│ ├── commands.md
│ ├── cider_docs_man.md
│ ├── cider_docs_md.md
│ ├── cider_docs_config.md
│ ├── cider_completions.md
│ ├── cider.md
│ ├── cider_check.md
│ ├── cider_docs.md
│ ├── cider_init.md
│ └── cider_release.md
├── _sass
│ └── custom
│ │ └── custom.scss
├── man
│ ├── cider.1
│ ├── cider_docs_man.1
│ ├── cider_docs_md.1
│ ├── cider_completions.1
│ ├── cider_docs_config.1
│ ├── cider_docs.1
│ ├── cider_check.1
│ ├── cider_init.1
│ └── cider_release.1
├── _includes
│ └── head_custom.html
├── _config.yml
├── index.md
├── install.md
├── faq.md
├── configuration-footer.md
└── quick-start.md
├── .golangci.yml
├── pkg
├── config
│ ├── testdata
│ │ ├── invalid.yml
│ │ └── valid.yml
│ ├── doc.go
│ └── config_test.go
└── context
│ ├── interrupt_test.go
│ ├── credentials_test.go
│ ├── credentials.go
│ ├── interrupt.go
│ ├── context_test.go
│ └── context.go
├── go.mod
├── .gitignore
├── .github
└── workflows
│ ├── docs.yml
│ └── build.yml
├── internal
├── clicommand
│ ├── import.go
│ ├── error_test.go
│ ├── error.go
│ ├── init_test.go
│ ├── root_test.go
│ ├── clicommand.go
│ ├── check_test.go
│ ├── config.go
│ ├── completions_test.go
│ ├── config_test.go
│ ├── completions.go
│ ├── check.go
│ └── root.go
├── defaults
│ ├── defaults_test.go
│ └── defaults.go
├── pipeline
│ ├── pipeline_test.go
│ └── pipeline.go
├── client
│ ├── project.go
│ ├── assets_test.go
│ ├── clienttest
│ │ └── clienttest_test.go
│ └── util_test.go
├── middleware
│ ├── middleware.go
│ ├── logging_test.go
│ ├── error.go
│ ├── error_test.go
│ └── logging.go
├── closer
│ ├── closer.go
│ └── closer_test.go
├── log
│ ├── log_test.go
│ └── log.go
├── pipe
│ ├── pipe_test.go
│ ├── defaults
│ │ ├── defaults.go
│ │ └── defaults_test.go
│ ├── semver
│ │ ├── semver.go
│ │ └── semver_test.go
│ ├── pipe.go
│ ├── testflight
│ │ ├── testflight_test.go
│ │ └── testflight.go
│ ├── publish
│ │ ├── publish.go
│ │ └── publish_test.go
│ ├── env
│ │ ├── env.go
│ │ └── env_test.go
│ └── store
│ │ └── store_test.go
├── git
│ ├── errors_test.go
│ ├── errors.go
│ ├── git.go
│ └── git_test.go
├── template
│ ├── template_test.go
│ └── template.go
├── shell
│ ├── shell_test.go
│ ├── shelltest
│ │ ├── shelltest_test.go
│ │ └── shelltest.go
│ └── shell.go
└── parallel
│ ├── group_test.go
│ └── group.go
├── tools
├── gendoc
│ ├── doc.go
│ ├── man.go
│ ├── md.go
│ ├── main.go
│ └── config.go
└── licensing
│ └── main.go
├── cmd
└── cider
│ ├── main.go
│ └── main_test.go
├── README.md
└── .goreleaser.yml
/docs/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cidertool/cider/HEAD/docs/favicon.ico
--------------------------------------------------------------------------------
/docs/htmltest.yml:
--------------------------------------------------------------------------------
1 | DirectoryPath: _site
2 | IgnoreInternalEmptyHash: true
3 | IgnoreSSLVerify: true
4 |
--------------------------------------------------------------------------------
/docs/assets/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cidertool/cider/HEAD/docs/assets/images/icon.png
--------------------------------------------------------------------------------
/docs/assets/images/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cidertool/cider/HEAD/docs/assets/images/banner.png
--------------------------------------------------------------------------------
/docs/assets/images/header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cidertool/cider/HEAD/docs/assets/images/header.png
--------------------------------------------------------------------------------
/docs/assets/images/favicon_120px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cidertool/cider/HEAD/docs/assets/images/favicon_120px.png
--------------------------------------------------------------------------------
/docs/assets/images/favicon_152px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cidertool/cider/HEAD/docs/assets/images/favicon_152px.png
--------------------------------------------------------------------------------
/docs/assets/images/favicon_167px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cidertool/cider/HEAD/docs/assets/images/favicon_167px.png
--------------------------------------------------------------------------------
/docs/assets/images/favicon_16px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cidertool/cider/HEAD/docs/assets/images/favicon_16px.png
--------------------------------------------------------------------------------
/docs/assets/images/favicon_180px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cidertool/cider/HEAD/docs/assets/images/favicon_180px.png
--------------------------------------------------------------------------------
/docs/assets/images/favicon_32px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cidertool/cider/HEAD/docs/assets/images/favicon_32px.png
--------------------------------------------------------------------------------
/docs/assets/images/favicon_96px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cidertool/cider/HEAD/docs/assets/images/favicon_96px.png
--------------------------------------------------------------------------------
/docs/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4 |
5 | gem "github-pages", "~> 214", group: :jekyll_plugins
6 | gem "jekyll-seo-tag", "~> 2.7"
7 | gem "just-the-docs"
8 |
--------------------------------------------------------------------------------
/docs/commands/commands.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | nav_order: 7
4 | has_children: true
5 | ---
6 |
7 | # Commands
8 |
9 | Please refer to the reference documentation for each command for more information, or consult the manpage if it's more accessible to you.
10 |
--------------------------------------------------------------------------------
/.golangci.yml:
--------------------------------------------------------------------------------
1 | run:
2 | timeout: 5m
3 | issues:
4 | max-per-linter: 0
5 | max-same-issues: 0
6 | exclude-use-default: false
7 | linters:
8 | enable-all: true
9 | disable:
10 | - cyclop
11 | - exhaustivestruct
12 | - funlen
13 | - gofumpt
14 | - interfacer
15 | - lll
16 | - maligned
17 | - scopelint
18 | - testpackage
19 | - wrapcheck
20 |
--------------------------------------------------------------------------------
/pkg/config/testdata/invalid.yml:
--------------------------------------------------------------------------------
1 | ---
2 | Wayfair:
3 | id: com.sky.ProjectApp
4 | localizations:
5 | - name: My App
6 | subtitle: congratulations
7 | privacyPolicy:
8 | text: go away
9 | url: https://google.com
10 | - name: 僕のアップ
11 | subtitle: おめでとう
12 | privacyPolicy:
13 | text: 消えろ
14 | url: https://google.co.jp
15 | versions: ~
16 |
--------------------------------------------------------------------------------
/docs/_sass/custom/custom.scss:
--------------------------------------------------------------------------------
1 | .main-content .task-list-item {
2 | display: block;
3 | }
4 |
5 | h4,
6 | .text-delta,
7 | h5,
8 | .text-epsilon,
9 | h6,
10 | .text-zeta {
11 | font-size: 18px !important;
12 | font-weight: inherit;
13 | text-transform: inherit;
14 | letter-spacing: inherit;
15 | }
16 |
17 | img.header {
18 | display: block;
19 | width: auto;
20 | height: 100px;
21 | margin: 0 auto 2em;
22 | }
23 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/cidertool/cider
2 |
3 | go 1.16
4 |
5 | require (
6 | github.com/Masterminds/semver/v3 v3.1.1
7 | github.com/alessio/shellescape v1.4.1
8 | github.com/apex/log v1.9.0
9 | github.com/cidertool/asc-go v0.5.1
10 | github.com/fatih/color v1.10.0
11 | github.com/hashicorp/go-multierror v1.1.1
12 | github.com/manifoldco/promptui v0.8.0
13 | github.com/spf13/cobra v1.1.3
14 | github.com/stretchr/testify v1.7.0
15 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
16 | gopkg.in/yaml.v2 v2.4.0
17 | )
18 |
--------------------------------------------------------------------------------
/docs/commands/cider_docs_man.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | parent: Commands
4 | title: docs man
5 | nav_order: 0
6 | nav_exclude: true
7 | ---
8 |
9 | ## cider docs man
10 |
11 | Generate man documentation for Cider.
12 |
13 | ```
14 | cider docs man [flags]
15 | ```
16 |
17 | ### Options
18 |
19 | ```
20 | -h, --help help for man
21 | ```
22 |
23 | ### Options inherited from parent commands
24 |
25 | ```
26 | --debug Enable debug mode
27 | ```
28 |
29 | ### SEE ALSO
30 |
31 | * [cider docs](/commands/cider_docs/) - Generate documentation for Cider
32 |
33 |
--------------------------------------------------------------------------------
/docs/commands/cider_docs_md.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | parent: Commands
4 | title: docs md
5 | nav_order: 0
6 | nav_exclude: true
7 | ---
8 |
9 | ## cider docs md
10 |
11 | Generate Markdown documentation for Cider.
12 |
13 | ```
14 | cider docs md [flags]
15 | ```
16 |
17 | ### Options
18 |
19 | ```
20 | -h, --help help for md
21 | ```
22 |
23 | ### Options inherited from parent commands
24 |
25 | ```
26 | --debug Enable debug mode
27 | ```
28 |
29 | ### SEE ALSO
30 |
31 | * [cider docs](/commands/cider_docs/) - Generate documentation for Cider
32 |
33 |
--------------------------------------------------------------------------------
/docs/commands/cider_docs_config.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | parent: Commands
4 | title: docs config
5 | nav_order: 0
6 | nav_exclude: true
7 | ---
8 |
9 | ## cider docs config
10 |
11 | Generate configuration file documentation for Cider.
12 |
13 | ```
14 | cider docs config [flags]
15 | ```
16 |
17 | ### Options
18 |
19 | ```
20 | -h, --help help for config
21 | ```
22 |
23 | ### Options inherited from parent commands
24 |
25 | ```
26 | --debug Enable debug mode
27 | ```
28 |
29 | ### SEE ALSO
30 |
31 | * [cider docs](/commands/cider_docs/) - Generate documentation for Cider
32 |
33 |
--------------------------------------------------------------------------------
/docs/man/cider.1:
--------------------------------------------------------------------------------
1 | .nh
2 | .TH "CIDER" "1" "Apr 2021" "" ""
3 |
4 | .SH NAME
5 | .PP
6 | cider \- Submit your builds to the Apple App Store in seconds
7 |
8 |
9 | .SH SYNOPSIS
10 | .PP
11 | \fBcider [flags]\fP
12 |
13 |
14 | .SH DESCRIPTION
15 | .PP
16 | Submit your builds to the Apple App Store in seconds
17 |
18 |
19 | .SH OPTIONS
20 | .PP
21 | \fB\-\-debug\fP[=false]
22 | Enable debug mode
23 |
24 | .PP
25 | \fB\-h\fP, \fB\-\-help\fP[=false]
26 | help for cider
27 |
28 |
29 | .SH SEE ALSO
30 | .PP
31 | \fBcider\-check(1)\fP, \fBcider\-completions(1)\fP, \fBcider\-init(1)\fP, \fBcider\-release(1)\fP
32 |
--------------------------------------------------------------------------------
/docs/commands/cider_completions.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | parent: Commands
4 | title: completions
5 | nav_order: 4
6 | nav_exclude: false
7 | ---
8 |
9 | ## cider completions
10 |
11 | Generate shell completions
12 |
13 | ```
14 | cider completions [bash|zsh|fish|powershell] [flags]
15 | ```
16 |
17 | ### Options
18 |
19 | ```
20 | -h, --help help for completions
21 | ```
22 |
23 | ### Options inherited from parent commands
24 |
25 | ```
26 | --debug Enable debug mode
27 | ```
28 |
29 | ### SEE ALSO
30 |
31 | * [cider](/commands/cider/) - Submit your builds to the Apple App Store in seconds
32 |
33 |
--------------------------------------------------------------------------------
/docs/man/cider_docs_man.1:
--------------------------------------------------------------------------------
1 | .nh
2 | .TH "CIDER\-DOCS\-MAN" "1" "Oct 2020" "Auto generated by spf13/cobra" ""
3 |
4 | .SH NAME
5 | .PP
6 | cider\-docs\-man \- Generate man documentation for Cider.
7 |
8 |
9 | .SH SYNOPSIS
10 | .PP
11 | \fBcider docs man [flags]\fP
12 |
13 |
14 | .SH DESCRIPTION
15 | .PP
16 | Generate man documentation for Cider.
17 |
18 |
19 | .SH OPTIONS
20 | .PP
21 | \fB\-h\fP, \fB\-\-help\fP[=false]
22 | help for man
23 |
24 |
25 | .SH OPTIONS INHERITED FROM PARENT COMMANDS
26 | .PP
27 | \fB\-\-debug\fP[=false]
28 | Enable debug mode
29 |
30 |
31 | .SH SEE ALSO
32 | .PP
33 | \fBcider\-docs(1)\fP
34 |
--------------------------------------------------------------------------------
/docs/man/cider_docs_md.1:
--------------------------------------------------------------------------------
1 | .nh
2 | .TH "CIDER\-DOCS\-MD" "1" "Oct 2020" "Auto generated by spf13/cobra" ""
3 |
4 | .SH NAME
5 | .PP
6 | cider\-docs\-md \- Generate Markdown documentation for Cider.
7 |
8 |
9 | .SH SYNOPSIS
10 | .PP
11 | \fBcider docs md [flags]\fP
12 |
13 |
14 | .SH DESCRIPTION
15 | .PP
16 | Generate Markdown documentation for Cider.
17 |
18 |
19 | .SH OPTIONS
20 | .PP
21 | \fB\-h\fP, \fB\-\-help\fP[=false]
22 | help for md
23 |
24 |
25 | .SH OPTIONS INHERITED FROM PARENT COMMANDS
26 | .PP
27 | \fB\-\-debug\fP[=false]
28 | Enable debug mode
29 |
30 |
31 | .SH SEE ALSO
32 | .PP
33 | \fBcider\-docs(1)\fP
34 |
--------------------------------------------------------------------------------
/docs/man/cider_completions.1:
--------------------------------------------------------------------------------
1 | .nh
2 | .TH "CIDER\-COMPLETIONS" "1" "Apr 2021" "Auto generated by spf13/cobra" ""
3 |
4 | .SH NAME
5 | .PP
6 | cider\-completions \- Generate shell completions
7 |
8 |
9 | .SH SYNOPSIS
10 | .PP
11 | \fBcider completions [bash|zsh|fish|powershell] [flags]\fP
12 |
13 |
14 | .SH DESCRIPTION
15 | .PP
16 | Generate shell completions
17 |
18 |
19 | .SH OPTIONS
20 | .PP
21 | \fB\-h\fP, \fB\-\-help\fP[=false]
22 | help for completions
23 |
24 |
25 | .SH OPTIONS INHERITED FROM PARENT COMMANDS
26 | .PP
27 | \fB\-\-debug\fP[=false]
28 | Enable debug mode
29 |
30 |
31 | .SH SEE ALSO
32 | .PP
33 | \fBcider(1)\fP
34 |
--------------------------------------------------------------------------------
/docs/man/cider_docs_config.1:
--------------------------------------------------------------------------------
1 | .nh
2 | .TH "CIDER\-DOCS\-CONFIG" "1" "Oct 2020" "Auto generated by spf13/cobra" ""
3 |
4 | .SH NAME
5 | .PP
6 | cider\-docs\-config \- Generate configuration file documentation for Cider.
7 |
8 |
9 | .SH SYNOPSIS
10 | .PP
11 | \fBcider docs config [flags]\fP
12 |
13 |
14 | .SH DESCRIPTION
15 | .PP
16 | Generate configuration file documentation for Cider.
17 |
18 |
19 | .SH OPTIONS
20 | .PP
21 | \fB\-h\fP, \fB\-\-help\fP[=false]
22 | help for config
23 |
24 |
25 | .SH OPTIONS INHERITED FROM PARENT COMMANDS
26 | .PP
27 | \fB\-\-debug\fP[=false]
28 | Enable debug mode
29 |
30 |
31 | .SH SEE ALSO
32 | .PP
33 | \fBcider\-docs(1)\fP
34 |
--------------------------------------------------------------------------------
/docs/man/cider_docs.1:
--------------------------------------------------------------------------------
1 | .nh
2 | .TH "CIDER\-DOCS" "1" "Oct 2020" "Auto generated by spf13/cobra" ""
3 |
4 | .SH NAME
5 | .PP
6 | cider\-docs \- Generate documentation for Cider
7 |
8 |
9 | .SH SYNOPSIS
10 | .PP
11 | \fBcider docs [flags]\fP
12 |
13 |
14 | .SH DESCRIPTION
15 | .PP
16 | Generate documentation for Cider
17 |
18 |
19 | .SH OPTIONS
20 | .PP
21 | \fB\-h\fP, \fB\-\-help\fP[=false]
22 | help for docs
23 |
24 |
25 | .SH OPTIONS INHERITED FROM PARENT COMMANDS
26 | .PP
27 | \fB\-\-debug\fP[=false]
28 | Enable debug mode
29 |
30 |
31 | .SH SEE ALSO
32 | .PP
33 | \fBcider(1)\fP, \fBcider\-docs\-config(1)\fP, \fBcider\-docs\-man(1)\fP, \fBcider\-docs\-md(1)\fP
34 |
--------------------------------------------------------------------------------
/docs/commands/cider.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | parent: Commands
4 | title: cider
5 | nav_order: 0
6 | nav_exclude: false
7 | ---
8 |
9 | ## cider
10 |
11 | Submit your builds to the Apple App Store in seconds
12 |
13 | ### Options
14 |
15 | ```
16 | --debug Enable debug mode
17 | -h, --help help for cider
18 | ```
19 |
20 | ### SEE ALSO
21 |
22 | * [cider check](/commands/cider_check/) - Checks if the configuration is valid
23 | * [cider completions](/commands/cider_completions/) - Generate shell completions
24 | * [cider init](/commands/cider_init/) - Generates a .cider.yml file
25 | * [cider release](/commands/cider_release/) - Release the selected apps in the current project
26 |
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore everything then restore any directories or files with extensions, so binaries are properly excluded
2 | *
3 | !*.*
4 | !*/
5 | !/COPYING
6 | !/Gemfile
7 | !Dockerfile*
8 |
9 | # Binaries for programs and plugins
10 | *.exe
11 | *.exe~
12 | *.dll
13 | *.so
14 | *.dylib
15 |
16 | # Test binary, built with `go test -c`
17 | *.test
18 |
19 | # Output of the go coverage tool
20 | *.out
21 |
22 | # Dependency directories (remove the comment below to include it)
23 | vendor/
24 |
25 | # Environment files, containing secrets
26 | .env
27 | *.p8
28 | .vscode/
29 | *.prettierrc*
30 |
31 | # System files
32 | Thumbs.db
33 | .DS_Store
34 | *.swp
35 |
36 | # Documentation
37 | docs/_site
38 | docs/bin
39 | docs/tmp
40 |
41 | # GoReleaser
42 | dist/
43 |
--------------------------------------------------------------------------------
/docs/commands/cider_check.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | parent: Commands
4 | title: check
5 | nav_order: 3
6 | nav_exclude: false
7 | ---
8 |
9 | ## cider check
10 |
11 | Checks if the configuration is valid
12 |
13 | ### Synopsis
14 |
15 | Use to validate your configuration file.
16 |
17 | ```
18 | cider check [flags]
19 | ```
20 |
21 | ### Examples
22 |
23 | ```
24 | cider check
25 | ```
26 |
27 | ### Options
28 |
29 | ```
30 | -f, --config string Configuration file to check
31 | -h, --help help for check
32 | ```
33 |
34 | ### Options inherited from parent commands
35 |
36 | ```
37 | --debug Enable debug mode
38 | ```
39 |
40 | ### SEE ALSO
41 |
42 | * [cider](/commands/cider/) - Submit your builds to the Apple App Store in seconds
43 |
44 |
--------------------------------------------------------------------------------
/docs/_includes/head_custom.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/docs/man/cider_check.1:
--------------------------------------------------------------------------------
1 | .nh
2 | .TH "CIDER\-CHECK" "1" "Apr 2021" "Auto generated by spf13/cobra" ""
3 |
4 | .SH NAME
5 | .PP
6 | cider\-check \- Checks if the configuration is valid
7 |
8 |
9 | .SH SYNOPSIS
10 | .PP
11 | \fBcider check [flags]\fP
12 |
13 |
14 | .SH DESCRIPTION
15 | .PP
16 | Use to validate your configuration file.
17 |
18 |
19 | .SH OPTIONS
20 | .PP
21 | \fB\-f\fP, \fB\-\-config\fP=""
22 | Configuration file to check
23 |
24 | .PP
25 | \fB\-h\fP, \fB\-\-help\fP[=false]
26 | help for check
27 |
28 |
29 | .SH OPTIONS INHERITED FROM PARENT COMMANDS
30 | .PP
31 | \fB\-\-debug\fP[=false]
32 | Enable debug mode
33 |
34 |
35 | .SH EXAMPLE
36 | .PP
37 | .RS
38 |
39 | .nf
40 | cider check
41 |
42 | .fi
43 | .RE
44 |
45 |
46 | .SH SEE ALSO
47 | .PP
48 | \fBcider(1)\fP
49 |
--------------------------------------------------------------------------------
/.github/workflows/docs.yml:
--------------------------------------------------------------------------------
1 | name: docs
2 | on:
3 | push:
4 | branches:
5 | - main
6 | paths:
7 | - 'docs/**'
8 | pull_request:
9 | paths:
10 | - 'docs/**'
11 |
12 | jobs:
13 | tests:
14 | name: test docs
15 | runs-on: ubuntu-latest
16 | steps:
17 | - uses: actions/checkout@v2
18 |
19 | - name: Build Site
20 | run: |
21 | docker run --rm \
22 | --env JEKYLL_UID=$(id -u) \
23 | --env JEKYLL_GID=$(id -g) \
24 | --volume="$PWD/docs:/srv/jekyll" \
25 | jekyll/jekyll:latest \
26 | jekyll build
27 |
28 | - name: Run htmltest
29 | run: |
30 | cd docs/
31 | curl https://htmltest.wjdp.uk | bash
32 | ./bin/htmltest -c htmltest.yml
33 |
--------------------------------------------------------------------------------
/internal/clicommand/import.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package clicommand
22 |
--------------------------------------------------------------------------------
/docs/commands/cider_docs.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | parent: Commands
4 | title: docs
5 | nav_order: 0
6 | nav_exclude: true
7 | ---
8 |
9 | ## cider docs
10 |
11 | Generate documentation for Cider
12 |
13 | ```
14 | cider docs [flags]
15 | ```
16 |
17 | ### Options
18 |
19 | ```
20 | -h, --help help for docs
21 | ```
22 |
23 | ### Options inherited from parent commands
24 |
25 | ```
26 | --debug Enable debug mode
27 | ```
28 |
29 | ### SEE ALSO
30 |
31 | * [cider](/commands/cider/) - Submit your builds to the Apple App Store in seconds
32 | * [cider docs config](/commands/cider_docs_config/) - Generate configuration file documentation for Cider.
33 | * [cider docs man](/commands/cider_docs_man/) - Generate man documentation for Cider.
34 | * [cider docs md](/commands/cider_docs_md/) - Generate Markdown documentation for Cider.
35 |
36 |
--------------------------------------------------------------------------------
/internal/defaults/defaults_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package defaults
22 |
23 | // this package has no testable statements
24 |
--------------------------------------------------------------------------------
/internal/pipeline/pipeline_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package pipeline
22 |
23 | // this package has no testable statements
24 |
--------------------------------------------------------------------------------
/docs/_config.yml:
--------------------------------------------------------------------------------
1 | title: Cider
2 | description: Cider documentation
3 | permalink: pretty
4 | remote_theme: pmarsceill/just-the-docs
5 | logo: assets/images/icon.png
6 |
7 | plugins:
8 | - jekyll-seo-tag
9 |
10 | heading_anchors: true
11 | aux_links:
12 | GitHub:
13 | - '//github.com/cidertool/cider'
14 | aux_links_new_tab: true
15 |
16 | footer_content: 'Copyright © 2020 Aaron Sky. Distributed under the GNU General Public License version 3.0 or later.'
17 | # last_edit_timestamp show or hide edit time - page must have `last_modified_date` defined in the frontmatter
18 | last_edit_timestamp: true
19 | # last_edit_time_format uses ruby's time format: https://ruby-doc.org/stdlib-2.7.0/libdoc/time/rdoc/Time.html
20 | last_edit_time_format: '%b %e %Y at %I:%M %p'
21 |
22 | exclude:
23 | - configuration-footer.md
24 | - man/*
25 | - bin/*
26 | - htmltest.yml
27 |
--------------------------------------------------------------------------------
/tools/gendoc/doc.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | // Package main handles the automatic generation of documentation for the command line interface
22 | // and configuration format.
23 | package main
24 |
--------------------------------------------------------------------------------
/docs/commands/cider_init.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | parent: Commands
4 | title: init
5 | nav_order: 1
6 | nav_exclude: false
7 | ---
8 |
9 | ## cider init
10 |
11 | Generates a .cider.yml file
12 |
13 | ### Synopsis
14 |
15 | Use to initialize a new Cider project. This will create a new configuration file
16 | in the current directory that should be checked into source control.
17 |
18 | ```
19 | cider init [flags]
20 | ```
21 |
22 | ### Examples
23 |
24 | ```
25 | cider init
26 | ```
27 |
28 | ### Options
29 |
30 | ```
31 | -f, --config string Path of configuration file to create (default ".cider.yml")
32 | -h, --help help for init
33 | -y, --skip-prompt Skips onboarding prompts. This can result in an overwritten configuration file
34 | ```
35 |
36 | ### Options inherited from parent commands
37 |
38 | ```
39 | --debug Enable debug mode
40 | ```
41 |
42 | ### SEE ALSO
43 |
44 | * [cider](/commands/cider/) - Submit your builds to the Apple App Store in seconds
45 |
46 |
--------------------------------------------------------------------------------
/internal/client/project.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package client
22 |
23 | import (
24 | "github.com/cidertool/cider/pkg/config"
25 | )
26 |
27 | func (c *ascClient) Project() (project *config.Project, err error) {
28 | return project, err
29 | }
30 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: home
3 | title: Home
4 | nav_order: 1
5 | description: 'Cider is a command-line application that makes it easy to submit your Apple App Store apps for review.'
6 | permalink: /
7 | ---
8 |
9 |
10 |
11 | Cider is a tool managing the entire release process of an iOS, macOS or tvOS application, supported by official Apple APIs. It takes the builds you've uploaded to App Store Connect, updates their metadata, and submits them for review automatically using an expressive YAML configuration. Unlike Xcode or altool, Cider is designed to be useful on Linux and Windows, in addition to macOS.
12 |
13 | Cider is not a replacement for `altool`, the official command-line interface for uploading, validating, and notarizing archives to Apple. It's instead designed to complement `altool`, and by extension `xcodebuild`. With Cider, your pipeline can build, test, upload, and now release your app without any required manual action.
14 |
--------------------------------------------------------------------------------
/docs/man/cider_init.1:
--------------------------------------------------------------------------------
1 | .nh
2 | .TH "CIDER\-INIT" "1" "Apr 2021" "Auto generated by spf13/cobra" ""
3 |
4 | .SH NAME
5 | .PP
6 | cider\-init \- Generates a .cider.yml file
7 |
8 |
9 | .SH SYNOPSIS
10 | .PP
11 | \fBcider init [flags]\fP
12 |
13 |
14 | .SH DESCRIPTION
15 | .PP
16 | Use to initialize a new Cider project. This will create a new configuration file
17 | in the current directory that should be checked into source control.
18 |
19 |
20 | .SH OPTIONS
21 | .PP
22 | \fB\-f\fP, \fB\-\-config\fP=".cider.yml"
23 | Path of configuration file to create
24 |
25 | .PP
26 | \fB\-h\fP, \fB\-\-help\fP[=false]
27 | help for init
28 |
29 | .PP
30 | \fB\-y\fP, \fB\-\-skip\-prompt\fP[=false]
31 | Skips onboarding prompts. This can result in an overwritten configuration file
32 |
33 |
34 | .SH OPTIONS INHERITED FROM PARENT COMMANDS
35 | .PP
36 | \fB\-\-debug\fP[=false]
37 | Enable debug mode
38 |
39 |
40 | .SH EXAMPLE
41 | .PP
42 | .RS
43 |
44 | .nf
45 | cider init
46 |
47 | .fi
48 | .RE
49 |
50 |
51 | .SH SEE ALSO
52 | .PP
53 | \fBcider(1)\fP
54 |
--------------------------------------------------------------------------------
/pkg/config/doc.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | /*
22 | Package config contains types and helpers available to configure a Cider project.
23 |
24 | You can customize your project using a `.cider.yml` file either created from scratch
25 | or using [`cider init`](./commands/cider_init.md).
26 | */
27 | package config
28 |
--------------------------------------------------------------------------------
/docs/install.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | nav_order: 2
4 | ---
5 |
6 | # Installation
7 |
8 | Cider can be installed from a variety of sources.
9 |
10 | ### Homebrew (macOS/Linux)
11 |
12 | ```shell
13 | brew install cidertool/tap/cider
14 | ```
15 |
16 | ### Scoop (Windows)
17 |
18 | ```shell
19 | scoop bucket add cider https://github.com/cidertool/scoop-bucket.git
20 | scoop install cider
21 | ```
22 |
23 | ### Docker
24 |
25 | ```shell
26 | docker run --rm \
27 | --volume $PWD:/app \
28 | --workdir /app \
29 | --env ASC_KEY_ID \
30 | --env ASC_ISSUER_ID \
31 | --env ASC_PRIVATE_KEY \
32 | cidertool/cider release
33 | ```
34 |
35 | ### Pre-built Binary
36 |
37 | Download the specific version for your platform on Cider's [releases page](https://github.com/cidertool/cider/releases).
38 |
39 | ### Compile from Source
40 |
41 | ```shell
42 | git clone git@github.com:cidertool/cider.git
43 | cd cider
44 | go build -o cider ./cmd/cider
45 | ```
46 |
47 | From there, you can use your locally-built Cider binary for whatever purposes you need. Cider requires **Go 1.16 or higher** to be installed.
48 |
49 | ## Missing your favorite?
50 |
51 | Please [file an issue](https://github.com/cidertool/cider/issues/new)!
52 |
--------------------------------------------------------------------------------
/internal/middleware/middleware.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | // Package middleware contains middleware for handling error and logging propagation
22 | package middleware
23 |
24 | import "github.com/cidertool/cider/pkg/context"
25 |
26 | // Action is a function that takes a context and returns an error.
27 | // It is is used on Pipers, Defaulters and Publishers, although they are not
28 | // aware of this generalization.
29 | type Action func(ctx *context.Context) error
30 |
--------------------------------------------------------------------------------
/internal/clicommand/error_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package clicommand
22 |
23 | import (
24 | "errors"
25 | "testing"
26 |
27 | "github.com/stretchr/testify/assert"
28 | )
29 |
30 | var errTestError = errors.New("TEST")
31 |
32 | func TestErrors(t *testing.T) {
33 | t.Parallel()
34 |
35 | err := wrapError(errTestError, "TEST")
36 | assert.Error(t, err)
37 | assert.Equal(t, "TEST", err.Error())
38 | assert.Equal(t, 1, err.code)
39 | assert.Equal(t, "TEST", err.details)
40 | }
41 |
--------------------------------------------------------------------------------
/internal/closer/closer.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | // Package closer is a helper for closing io.Closers.
22 | package closer
23 |
24 | import (
25 | "io"
26 | "log"
27 | )
28 |
29 | type closeErrFunc func(error)
30 |
31 | // nolint:gochecknoglobals
32 | var onCloseErr closeErrFunc = func(err error) { log.Fatal(err) }
33 |
34 | // Close closes an io.Closer and handles the possible Close error.
35 | func Close(c io.Closer) {
36 | if err := c.Close(); err != nil {
37 | onCloseErr(err)
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/internal/clicommand/error.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package clicommand
22 |
23 | type exitError struct {
24 | err error
25 | code int
26 | details string
27 | }
28 |
29 | func wrapErrorWithCode(err error, code int, details string) *exitError {
30 | return &exitError{
31 | err: err,
32 | code: code,
33 | details: details,
34 | }
35 | }
36 |
37 | func wrapError(err error, log string) *exitError {
38 | return wrapErrorWithCode(err, 1, log)
39 | }
40 |
41 | func (e *exitError) Error() string {
42 | return e.err.Error()
43 | }
44 |
--------------------------------------------------------------------------------
/internal/clicommand/init_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package clicommand
22 |
23 | import (
24 | "path/filepath"
25 | "testing"
26 |
27 | "github.com/stretchr/testify/assert"
28 | )
29 |
30 | func TestInitCmd(t *testing.T) {
31 | t.Parallel()
32 |
33 | var folder = t.TempDir()
34 |
35 | var noDebug bool
36 |
37 | var cmd = newInitCmd(&noDebug).cmd
38 |
39 | var path = filepath.Join(folder, "foo.yaml")
40 |
41 | cmd.SetArgs([]string{"-f", path, "--skip-prompt"})
42 | assert.NoError(t, cmd.Execute())
43 | assert.FileExists(t, path)
44 | }
45 |
--------------------------------------------------------------------------------
/internal/middleware/logging_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package middleware
22 |
23 | import (
24 | "testing"
25 |
26 | "github.com/cidertool/cider/pkg/config"
27 | "github.com/cidertool/cider/pkg/context"
28 | "github.com/stretchr/testify/assert"
29 | )
30 |
31 | func TestLogging(t *testing.T) {
32 | t.Parallel()
33 |
34 | ctx := context.New(config.Project{})
35 |
36 | wrapped := Logging("TEST", func(ctx *context.Context) error {
37 | return nil
38 | }, DefaultInitialPadding)
39 |
40 | err := wrapped(ctx)
41 | assert.NoError(t, err)
42 | }
43 |
--------------------------------------------------------------------------------
/internal/clicommand/root_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package clicommand
22 |
23 | import (
24 | "testing"
25 |
26 | "github.com/stretchr/testify/assert"
27 | )
28 |
29 | func TestRootCmd(t *testing.T) {
30 | t.Parallel()
31 |
32 | exit := func(code int) {
33 | assert.Equal(t, 0, code)
34 | }
35 |
36 | Execute("TEST", exit, []string{"help", "--debug"})
37 | }
38 |
39 | func TestRootCmd_Error(t *testing.T) {
40 | t.Parallel()
41 |
42 | exit := func(code int) {
43 | assert.NotEqual(t, 0, code)
44 | }
45 |
46 | Execute("TEST", exit, []string{"check"})
47 | }
48 |
--------------------------------------------------------------------------------
/internal/clicommand/clicommand.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | // Package clicommand declares the command line interface for Cider.
22 | package clicommand
23 |
24 | import (
25 | "os"
26 |
27 | "github.com/cidertool/cider/internal/log"
28 | "github.com/fatih/color"
29 | )
30 |
31 | func newLogger(debugFlagValue *bool) *log.Log {
32 | logger := log.New()
33 |
34 | if os.Getenv("CI") != "" && color.NoColor {
35 | logger.SetColorMode(false)
36 | }
37 |
38 | if debugFlagValue != nil {
39 | logger.SetDebug(*debugFlagValue)
40 | logger.Debug("debug logs enabled")
41 | }
42 |
43 | return logger
44 | }
45 |
--------------------------------------------------------------------------------
/internal/middleware/error.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package middleware
22 |
23 | import (
24 | "github.com/cidertool/cider/internal/pipe"
25 | "github.com/cidertool/cider/pkg/context"
26 | )
27 |
28 | // ErrHandler handles an action error, ignoring and logging pipe skipped
29 | // errors.
30 | func ErrHandler(action Action) Action {
31 | return func(ctx *context.Context) error {
32 | var err = action(ctx)
33 | if err == nil {
34 | return nil
35 | }
36 |
37 | if pipe.IsSkip(err) {
38 | ctx.Log.WithError(err).Warn("pipe skipped")
39 |
40 | return nil
41 | }
42 |
43 | return err
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/internal/log/log_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package log
22 |
23 | import (
24 | "testing"
25 |
26 | alog "github.com/apex/log"
27 | "github.com/fatih/color"
28 | "github.com/stretchr/testify/assert"
29 | )
30 |
31 | func TestLog(t *testing.T) {
32 | t.Parallel()
33 |
34 | log := New()
35 |
36 | log.SetPadding(4)
37 |
38 | log.SetColorMode(false)
39 | assert.False(t, color.NoColor)
40 | log.SetColorMode(true)
41 | assert.True(t, color.NoColor)
42 |
43 | log.SetDebug(true)
44 | assert.Equal(t, log.Level, alog.DebugLevel)
45 | log.SetDebug(false)
46 | assert.Equal(t, log.Level, alog.InfoLevel)
47 | }
48 |
--------------------------------------------------------------------------------
/internal/defaults/defaults.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | // Package defaults is used by pipes to provide default values to a context configuration
22 | package defaults
23 |
24 | import (
25 | "fmt"
26 |
27 | "github.com/cidertool/cider/pkg/context"
28 | )
29 |
30 | // Defaulter can be implemented by a Piper to set default values for its
31 | // configuration.
32 | type Defaulter interface {
33 | fmt.Stringer
34 |
35 | // Default sets the configuration defaults
36 | Default(ctx *context.Context) error
37 | }
38 |
39 | // Defaulters is the list of defaulters
40 | // nolint: gochecknoglobals
41 | var Defaulters = []Defaulter{}
42 |
--------------------------------------------------------------------------------
/pkg/context/interrupt_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package context
22 |
23 | import (
24 | "context"
25 | "errors"
26 | "testing"
27 |
28 | "github.com/stretchr/testify/assert"
29 | )
30 |
31 | var errTestError = errors.New("TEST")
32 |
33 | func TestInterruptOK(t *testing.T) {
34 | t.Parallel()
35 |
36 | assert.NoError(t, NewInterrupt().Run(context.Background(), func() error {
37 | return nil
38 | }))
39 | }
40 |
41 | func TestInterruptErrors(t *testing.T) {
42 | t.Parallel()
43 |
44 | assert.EqualError(t, NewInterrupt().Run(context.Background(), func() error {
45 | return errTestError
46 | }), errTestError.Error())
47 | }
48 |
--------------------------------------------------------------------------------
/internal/pipe/pipe_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package pipe
22 |
23 | import (
24 | "testing"
25 |
26 | "github.com/stretchr/testify/assert"
27 | )
28 |
29 | func TestPipeSkip(t *testing.T) {
30 | t.Parallel()
31 |
32 | skip := Skip("TEST")
33 |
34 | var err error = ErrSkip{reason: "TEST"}
35 |
36 | assert.Error(t, skip)
37 | assert.Error(t, err)
38 | assert.True(t, IsSkip(err))
39 | assert.Equal(t, err, skip)
40 | assert.EqualError(t, err, skip.Error())
41 | }
42 |
43 | func TestErrMissingApp(t *testing.T) {
44 | t.Parallel()
45 |
46 | err := ErrMissingApp{Name: "TEST"}
47 | assert.Error(t, err)
48 | assert.EqualError(t, err, "no app defined in configuration matching the name TEST")
49 | }
50 |
--------------------------------------------------------------------------------
/internal/clicommand/check_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package clicommand
22 |
23 | import (
24 | "os"
25 | "path/filepath"
26 | "testing"
27 |
28 | "github.com/cidertool/cider/pkg/config"
29 | "github.com/stretchr/testify/assert"
30 | )
31 |
32 | func TestCheckCmd(t *testing.T) {
33 | t.Parallel()
34 |
35 | var noDebug bool
36 |
37 | var cmd = newCheckCmd(&noDebug)
38 |
39 | var path = filepath.Join(t.TempDir(), "foo.yaml")
40 |
41 | var proj config.Project
42 |
43 | s, err := proj.String()
44 | assert.NoError(t, err)
45 | err = os.WriteFile(path, []byte(s), 0600)
46 | assert.NoError(t, err)
47 |
48 | err = cmd.cmd.Execute()
49 | assert.Error(t, err)
50 |
51 | cmd.config = path
52 |
53 | err = cmd.cmd.Execute()
54 | assert.NoError(t, err)
55 | }
56 |
--------------------------------------------------------------------------------
/pkg/context/credentials_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package context
22 |
23 | import (
24 | "testing"
25 |
26 | "github.com/stretchr/testify/assert"
27 | )
28 |
29 | func TestNewCredentials(t *testing.T) {
30 | t.Parallel()
31 |
32 | privateKey := []byte(`
33 | -----BEGIN PRIVATE KEY-----
34 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgTHOfkv1Dj2Yp8hyT
35 | cqY/BRRnYsoLzQoT04EW9d57dd+hRANCAAReUxyqGRpXQI2Fe833j7gQTnZ002VO
36 | FSEpv2QUFs0+dXz04SWVmmzFErM0/iQyCYom0V1IMOWgV/8xvFN6+AeX
37 | -----END PRIVATE KEY-----
38 | `)
39 | cred, err := NewCredentials("kid", "iss", privateKey)
40 | assert.NoError(t, err)
41 | assert.NotNil(t, cred)
42 | assert.NotNil(t, cred.Client())
43 |
44 | _, err = NewCredentials("kid", "iss", []byte("nothing"))
45 | assert.Error(t, err)
46 | }
47 |
--------------------------------------------------------------------------------
/internal/closer/closer_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package closer
22 |
23 | import (
24 | "errors"
25 | "testing"
26 |
27 | "github.com/stretchr/testify/assert"
28 | )
29 |
30 | var errAlreadyClosed = errors.New("already closed")
31 |
32 | func TestClose(t *testing.T) {
33 | t.Parallel()
34 |
35 | var expectingErr bool
36 |
37 | onCloseErr = func(err error) {
38 | if !expectingErr {
39 | assert.FailNow(t, err.Error())
40 | }
41 | }
42 |
43 | r := &resource{name: "a"}
44 | Close(r)
45 |
46 | r = &resource{name: "b"}
47 | Close(r)
48 |
49 | expectingErr = true
50 |
51 | Close(r)
52 | }
53 |
54 | type resource struct {
55 | name string
56 | closed bool
57 | }
58 |
59 | func (r *resource) Close() error {
60 | if r.closed {
61 | return errAlreadyClosed
62 | }
63 |
64 | r.closed = true
65 |
66 | return nil
67 | }
68 |
--------------------------------------------------------------------------------
/pkg/context/credentials.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package context
22 |
23 | import (
24 | "fmt"
25 | "net/http"
26 | "time"
27 |
28 | "github.com/cidertool/asc-go/asc"
29 | )
30 |
31 | const twentyMinuteTokenLifetime = time.Minute * 20
32 |
33 | // Credentials stores credentials used by clients.
34 | type Credentials interface {
35 | Client() *http.Client
36 | }
37 |
38 | type credentials struct {
39 | *asc.AuthTransport
40 | }
41 |
42 | // NewCredentials returns a new store object for App Store Connect credentials.
43 | func NewCredentials(keyID, issuerID string, privateKey []byte) (Credentials, error) {
44 | token, err := asc.NewTokenConfig(keyID, issuerID, twentyMinuteTokenLifetime, privateKey)
45 | if err != nil {
46 | err = fmt.Errorf("failed to authorize with App Store Connect: %w", err)
47 | }
48 |
49 | return credentials{token}, err
50 | }
51 |
--------------------------------------------------------------------------------
/internal/clicommand/config.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package clicommand
22 |
23 | import (
24 | "errors"
25 | "os"
26 | "path/filepath"
27 |
28 | "github.com/cidertool/cider/pkg/config"
29 | )
30 |
31 | // ErrConfigNotFound happens if a config file could not be found at any of the default locations.
32 | var ErrConfigNotFound = errors.New("config file not found at any default path")
33 |
34 | func loadConfig(path string, wd string) (config.Project, error) {
35 | if path != "" {
36 | return config.Load(path)
37 | }
38 |
39 | for _, f := range [4]string{
40 | ".cider.yml",
41 | ".cider.yaml",
42 | "cider.yml",
43 | "cider.yaml",
44 | } {
45 | proj, err := config.Load(filepath.Join(wd, f))
46 | if err != nil && os.IsNotExist(err) {
47 | continue
48 | }
49 |
50 | return proj, err
51 | }
52 |
53 | return config.Project{}, ErrConfigNotFound
54 | }
55 |
--------------------------------------------------------------------------------
/tools/gendoc/man.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package main
22 |
23 | import (
24 | "os"
25 | "path/filepath"
26 |
27 | "github.com/apex/log"
28 | "github.com/cidertool/cider/internal/clicommand"
29 | "github.com/spf13/cobra"
30 | "github.com/spf13/cobra/doc"
31 | )
32 |
33 | func runDocsManCmd(cmd *cobra.Command, args []string) error {
34 | var path string
35 | if len(args) == 0 {
36 | path = defaultDocsPath
37 | } else {
38 | path = args[0]
39 | }
40 |
41 | path = filepath.Join(path, "man")
42 |
43 | log.WithField("path", path).Info("generating man documentation")
44 |
45 | err := doc.GenManTreeFromOpts(clicommand.NewRoot("dev", os.Exit).Cmd, doc.GenManTreeOptions{
46 | Path: path,
47 | })
48 | if err != nil {
49 | log.Error("generation failed")
50 | } else {
51 | log.Info("generation completed successfully")
52 | }
53 |
54 | return err
55 | }
56 |
--------------------------------------------------------------------------------
/internal/clicommand/completions_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package clicommand
22 |
23 | import (
24 | "testing"
25 |
26 | "github.com/stretchr/testify/assert"
27 | )
28 |
29 | func TestCompletionsCmd(t *testing.T) {
30 | t.Parallel()
31 |
32 | cmd := newCompletionsCmd()
33 |
34 | cmd.cmd.SetArgs([]string{})
35 | err := cmd.cmd.Execute()
36 | assert.Error(t, err)
37 |
38 | cmd.cmd.SetArgs([]string{"bash"})
39 | err = cmd.cmd.Execute()
40 | assert.NoError(t, err)
41 |
42 | cmd.cmd.SetArgs([]string{"zsh"})
43 | err = cmd.cmd.Execute()
44 | assert.NoError(t, err)
45 |
46 | cmd.cmd.SetArgs([]string{"fish"})
47 | err = cmd.cmd.Execute()
48 | assert.NoError(t, err)
49 |
50 | cmd.cmd.SetArgs([]string{"powershell"})
51 | err = cmd.cmd.Execute()
52 | assert.NoError(t, err)
53 |
54 | cmd.cmd.SetArgs([]string{"oil"})
55 | err = cmd.cmd.Execute()
56 | assert.Error(t, err)
57 | }
58 |
--------------------------------------------------------------------------------
/internal/git/errors_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package git
22 |
23 | import (
24 | "testing"
25 |
26 | "github.com/stretchr/testify/assert"
27 | )
28 |
29 | func TestErrDirtyMessage(t *testing.T) {
30 | t.Parallel()
31 |
32 | err := ErrDirty{"TEST"}
33 | expected := "git is currently in a dirty state, please check in your pipeline what can be changing the following files:\nTEST"
34 | assert.Equal(t, expected, err.Error())
35 | }
36 |
37 | func TestErrWrongRefMessage(t *testing.T) {
38 | t.Parallel()
39 |
40 | err := ErrWrongRef{"TEST", "TEST"}
41 | expected := "git tag TEST was not made against commit TEST"
42 | assert.Equal(t, expected, err.Error())
43 | }
44 |
45 | func TestErrNotRepositoryMessage(t *testing.T) {
46 | t.Parallel()
47 |
48 | err := ErrNotRepository{"TEST"}
49 | expected := "the directory at TEST is not a git repository"
50 | assert.Equal(t, expected, err.Error())
51 | }
52 |
--------------------------------------------------------------------------------
/internal/pipe/defaults/defaults.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | // Package defaults runs all defaulter pipelines
22 | package defaults
23 |
24 | import (
25 | "github.com/cidertool/cider/internal/defaults"
26 | "github.com/cidertool/cider/internal/middleware"
27 | "github.com/cidertool/cider/pkg/context"
28 | )
29 |
30 | // Pipe that sets the defaults.
31 | type Pipe struct {
32 | defaulters []defaults.Defaulter
33 | }
34 |
35 | func (Pipe) String() string {
36 | return "setting defaults"
37 | }
38 |
39 | // Run the pipe.
40 | func (p Pipe) Run(ctx *context.Context) error {
41 | if len(p.defaulters) == 0 {
42 | p.defaulters = defaults.Defaulters
43 | }
44 |
45 | for _, defaulter := range p.defaulters {
46 | if err := middleware.Logging(
47 | defaulter.String(),
48 | middleware.ErrHandler(defaulter.Default),
49 | middleware.ExtraPadding,
50 | )(ctx); err != nil {
51 | return err
52 | }
53 | }
54 |
55 | return nil
56 | }
57 |
--------------------------------------------------------------------------------
/internal/pipe/semver/semver.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | // Package semver is a pipe that parses a version string into semver components
22 | package semver
23 |
24 | import (
25 | "fmt"
26 |
27 | "github.com/Masterminds/semver/v3"
28 | "github.com/cidertool/cider/pkg/context"
29 | )
30 |
31 | // Pipe is a global hook pipe.
32 | type Pipe struct{}
33 |
34 | // String is the name of this pipe.
35 | func (Pipe) String() string {
36 | return "parsing version"
37 | }
38 |
39 | // Run executes the hooks.
40 | func (p Pipe) Run(ctx *context.Context) error {
41 | sv, err := semver.NewVersion(ctx.Version)
42 | if err != nil {
43 | return fmt.Errorf("failed to parse tag %s as semver: %w", ctx.Version, err)
44 | }
45 |
46 | ctx.Semver = context.Semver{
47 | Major: sv.Major(),
48 | Minor: sv.Minor(),
49 | Patch: sv.Patch(),
50 | Prerelease: sv.Prerelease(),
51 | RawVersion: sv.Original(),
52 | }
53 |
54 | return nil
55 | }
56 |
--------------------------------------------------------------------------------
/internal/pipe/defaults/defaults_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package defaults
22 |
23 | import (
24 | "errors"
25 | "testing"
26 |
27 | "github.com/cidertool/cider/internal/defaults"
28 | "github.com/cidertool/cider/pkg/config"
29 | "github.com/cidertool/cider/pkg/context"
30 | "github.com/stretchr/testify/assert"
31 | )
32 |
33 | var errTestError = errors.New("TEST")
34 |
35 | func TestDefaults(t *testing.T) {
36 | t.Parallel()
37 |
38 | ctx := context.New(config.Project{})
39 |
40 | pipe := Pipe{}
41 |
42 | var err error
43 |
44 | assert.Equal(t, "setting defaults", pipe.String())
45 |
46 | err = pipe.Run(ctx)
47 | assert.NoError(t, err)
48 |
49 | pipe.defaulters = []defaults.Defaulter{
50 | mockDefaulter{},
51 | }
52 | err = pipe.Run(ctx)
53 | assert.Error(t, err)
54 | }
55 |
56 | type mockDefaulter struct{}
57 |
58 | func (d mockDefaulter) String() string {
59 | return ""
60 | }
61 |
62 | func (d mockDefaulter) Default(ctx *context.Context) error {
63 | return errTestError
64 | }
65 |
--------------------------------------------------------------------------------
/internal/pipeline/pipeline.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | // Package pipeline stores the top-level pipeline and Piper interface used by most pipes
22 | package pipeline
23 |
24 | import (
25 | "fmt"
26 |
27 | "github.com/cidertool/cider/internal/pipe/defaults"
28 | "github.com/cidertool/cider/internal/pipe/env"
29 | "github.com/cidertool/cider/internal/pipe/git"
30 | "github.com/cidertool/cider/internal/pipe/publish"
31 | "github.com/cidertool/cider/internal/pipe/semver"
32 | "github.com/cidertool/cider/internal/pipe/template"
33 | "github.com/cidertool/cider/pkg/context"
34 | )
35 |
36 | // Piper defines a pipe, which can be part of a pipeline (a serie of pipes).
37 | type Piper interface {
38 | fmt.Stringer
39 |
40 | // Run the pipe
41 | Run(ctx *context.Context) error
42 | }
43 |
44 | // Pipeline contains all pipe implementations in order
45 | // nolint: gochecknoglobals
46 | var Pipeline = []Piper{
47 | env.Pipe{},
48 | git.Pipe{},
49 | semver.Pipe{},
50 | template.Pipe{},
51 | defaults.Pipe{},
52 | publish.Pipe{},
53 | }
54 |
--------------------------------------------------------------------------------
/internal/template/template_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package template
22 |
23 | import (
24 | "testing"
25 |
26 | "github.com/cidertool/cider/pkg/config"
27 | "github.com/cidertool/cider/pkg/context"
28 | "github.com/stretchr/testify/assert"
29 | )
30 |
31 | func TestTemplate(t *testing.T) {
32 | t.Parallel()
33 |
34 | tmpl, err := New(context.New(config.Project{})).
35 | WithEnv(map[string]string{
36 | "DOG": "HAPPY",
37 | "CAT": "GRUMPY",
38 | }).
39 | WithShellEnv("HORSE=EVIL", "CAT=SPOOKY").
40 | WithFields(Fields{
41 | "customKey": 0,
42 | }).
43 | Apply(`My {{ .env.CAT }} cat fought {{ .customKey }} {{ .env.HORSE }} horses.`)
44 | assert.NoError(t, err)
45 | assert.Equal(t, "My SPOOKY cat fought 0 EVIL horses.", tmpl)
46 | }
47 |
48 | func TestInvalidTemplate(t *testing.T) {
49 | t.Parallel()
50 |
51 | _, err := New(context.New(config.Project{})).Apply(`{{ .timestamp`)
52 | assert.Error(t, err)
53 | }
54 |
55 | func TestEmptyTemplate(t *testing.T) {
56 | t.Parallel()
57 |
58 | tmpl, err := New(context.New(config.Project{})).Apply("")
59 | assert.NoError(t, err)
60 | assert.Empty(t, tmpl)
61 | }
62 |
--------------------------------------------------------------------------------
/cmd/cider/main.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package main
22 |
23 | import (
24 | "fmt"
25 | "os"
26 |
27 | "github.com/cidertool/cider/internal/clicommand"
28 | )
29 |
30 | const licenseDisclaimer = `
31 | Copyright (C) 2020 Aaron Sky
32 | License GPLv3+: GNU GPL version 3 or later
33 |
34 | This is free software; you are free to change and redistribute it.
35 | There is NO WARRANTY, to the extent permitted by law.`
36 |
37 | // nolint: gochecknoglobals
38 | var (
39 | version = "dev"
40 | commit = ""
41 | date = ""
42 | builtBy = ""
43 | )
44 |
45 | func main() {
46 | clicommand.Execute(
47 | buildVersion(version, commit, date, builtBy),
48 | os.Exit,
49 | os.Args[1:],
50 | )
51 | }
52 |
53 | func buildVersion(version, commit, date, builtBy string) string {
54 | var result = version
55 |
56 | if commit != "" {
57 | result = fmt.Sprintf("%s\ncommit: %s", result, commit)
58 | }
59 |
60 | if date != "" {
61 | result = fmt.Sprintf("%s\nbuilt at: %s", result, date)
62 | }
63 |
64 | if builtBy != "" {
65 | result = fmt.Sprintf("%s\nbuilt by: %s", result, builtBy)
66 | }
67 |
68 | return result + licenseDisclaimer
69 | }
70 |
--------------------------------------------------------------------------------
/internal/middleware/error_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package middleware
22 |
23 | import (
24 | "errors"
25 | "testing"
26 |
27 | "github.com/cidertool/cider/internal/pipe"
28 | "github.com/cidertool/cider/pkg/config"
29 | "github.com/cidertool/cider/pkg/context"
30 | "github.com/stretchr/testify/assert"
31 | )
32 |
33 | var errTestError = errors.New("TEST")
34 |
35 | func TestErrHandler_WrapsError(t *testing.T) {
36 | t.Parallel()
37 |
38 | ctx := context.New(config.Project{})
39 |
40 | wrapped := ErrHandler(func(ctx *context.Context) error {
41 | return errTestError
42 | })
43 |
44 | err := wrapped(ctx)
45 | assert.Error(t, err)
46 | }
47 |
48 | func TestErrHandler_IgnoresNoError(t *testing.T) {
49 | t.Parallel()
50 |
51 | ctx := context.New(config.Project{})
52 |
53 | wrapped := ErrHandler(func(ctx *context.Context) error {
54 | return nil
55 | })
56 |
57 | err := wrapped(ctx)
58 | assert.NoError(t, err)
59 | }
60 |
61 | func TestErrHandler_HandlesSkip(t *testing.T) {
62 | t.Parallel()
63 |
64 | ctx := context.New(config.Project{})
65 |
66 | wrapped := ErrHandler(func(ctx *context.Context) error {
67 | return pipe.Skip("TEST")
68 | })
69 |
70 | err := wrapped(ctx)
71 | assert.NoError(t, err)
72 | }
73 |
--------------------------------------------------------------------------------
/internal/pipe/semver/semver_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package semver
22 |
23 | import (
24 | "testing"
25 |
26 | "github.com/cidertool/cider/pkg/config"
27 | "github.com/cidertool/cider/pkg/context"
28 | "github.com/stretchr/testify/assert"
29 | )
30 |
31 | func TestSemver(t *testing.T) {
32 | t.Parallel()
33 |
34 | ctx := context.New(config.Project{})
35 | pipe := Pipe{}
36 |
37 | var err error
38 |
39 | assert.Equal(t, "parsing version", pipe.String())
40 |
41 | err = pipe.Run(ctx)
42 | assert.Error(t, err)
43 | assert.Empty(t, ctx.Semver)
44 |
45 | ctx.Version = "1.0.1"
46 | err = pipe.Run(ctx)
47 | assert.NoError(t, err)
48 | assert.Equal(t, context.Semver{
49 | Major: 1,
50 | Minor: 0,
51 | Patch: 1,
52 | RawVersion: "1.0.1",
53 | }, ctx.Semver)
54 |
55 | ctx.Version = "1.1.1-patch90"
56 | err = pipe.Run(ctx)
57 | assert.NoError(t, err)
58 | assert.Equal(t, context.Semver{
59 | Major: 1,
60 | Minor: 1,
61 | Patch: 1,
62 | RawVersion: "1.1.1-patch90",
63 | Prerelease: "patch90",
64 | }, ctx.Semver)
65 |
66 | ctx.Version = "aa.ee.bb"
67 | ctx.Semver = context.Semver{}
68 | err = pipe.Run(ctx)
69 | assert.Error(t, err)
70 | assert.Empty(t, ctx.Semver)
71 | }
72 |
--------------------------------------------------------------------------------
/internal/clicommand/config_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package clicommand
22 |
23 | import (
24 | "os"
25 | "path/filepath"
26 | "testing"
27 |
28 | "github.com/cidertool/cider/pkg/config"
29 | "github.com/stretchr/testify/assert"
30 | )
31 |
32 | func TestConfig_Happy_CustomPath(t *testing.T) {
33 | t.Parallel()
34 |
35 | var path = filepath.Join(t.TempDir(), "foo.yaml")
36 |
37 | var proj config.Project
38 |
39 | s, err := proj.String()
40 | assert.NoError(t, err)
41 | err = os.WriteFile(path, []byte(s), 0600)
42 | assert.NoError(t, err)
43 | cfg, err := loadConfig(path, "")
44 | assert.NoError(t, err)
45 | assert.Empty(t, cfg)
46 | }
47 |
48 | func TestConfig_Happy_DefaultPath(t *testing.T) {
49 | t.Parallel()
50 |
51 | var folder = t.TempDir()
52 |
53 | var path = filepath.Join(folder, "cider.yaml")
54 |
55 | var proj config.Project
56 |
57 | s, err := proj.String()
58 | assert.NoError(t, err)
59 | err = os.WriteFile(path, []byte(s), 0600)
60 | assert.NoError(t, err)
61 | cfg, err := loadConfig("", folder)
62 | assert.NoError(t, err)
63 | assert.Empty(t, cfg)
64 | }
65 |
66 | func TestConfig_Err_DoesntExist(t *testing.T) {
67 | t.Parallel()
68 |
69 | cfg, err := loadConfig("", t.TempDir())
70 | assert.Error(t, err)
71 | assert.Empty(t, cfg)
72 | }
73 |
--------------------------------------------------------------------------------
/internal/clicommand/completions.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package clicommand
22 |
23 | import (
24 | "errors"
25 | "os"
26 |
27 | "github.com/spf13/cobra"
28 | )
29 |
30 | // ErrUnsupportedShell happens when a shell that Cobra does not support is
31 | // passed for the completions command.
32 | var ErrUnsupportedShell = errors.New("shell for completions is unsupported")
33 |
34 | type completionsCmd struct {
35 | cmd *cobra.Command
36 | }
37 |
38 | func newCompletionsCmd() *completionsCmd {
39 | var root = &completionsCmd{}
40 |
41 | var cmd = &cobra.Command{
42 | Use: "completions [bash|zsh|fish|powershell]",
43 | Short: "Generate shell completions",
44 | ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
45 | Args: cobra.ExactValidArgs(1),
46 | RunE: func(cmd *cobra.Command, args []string) error {
47 | switch args[0] {
48 | case "bash":
49 | return cmd.Root().GenBashCompletion(os.Stdout)
50 | case "zsh":
51 | return cmd.Root().GenZshCompletion(os.Stdout)
52 | case "fish":
53 | return cmd.Root().GenFishCompletion(os.Stdout, true)
54 | case "powershell":
55 | return cmd.Root().GenPowerShellCompletion(os.Stdout)
56 | }
57 |
58 | return ErrUnsupportedShell
59 | },
60 | }
61 |
62 | root.cmd = cmd
63 |
64 | return root
65 | }
66 |
--------------------------------------------------------------------------------
/docs/faq.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: FAQ
4 | nav_order: 6
5 | ---
6 |
7 | # Frequently Asked Questions
8 |
9 | ## Can I use Cider to upload builds?
10 |
11 | Cider is not designed to replace `altool`, the recommended Apple-provided tool for validating and uploading archives to Xcode. That tool has an exceptionally well-documented manpage and comes with stability guarantees from the platform-holder. Please use Cider to submit your builds once they've been uploaded and completed processing.
12 |
13 | ## Can I use an Apple ID to authenticate?
14 |
15 | No. Cider is built on the backbone of Apple's official App Store Connect API, and a valid [JSON Web Token](https://tools.ietf.org/html/rfc7519) generated from an issuer ID, key ID and private key are essential components in generating a compliant token. Create a key for a user on your team with the **App Manager** role by [following Apple's documentation](https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api) and use those credentials in your CI environment. If you ever need to revoke these credentials, you can do so by following [these instructions](https://developer.apple.com/documentation/appstoreconnectapi/revoking_api_keys).
16 |
17 | ## How is this different from Fastlane/Spaceship?
18 |
19 | Spaceship, and by extension Fastlane, are designed to be customizable for a variety of features and functions. You can do largely do anything, but that comes with the inherent overhead that "anything" entails. Spaceship has served Fastlane and the broader Apple development community well for years, but the investment cost can't be denied. Additionally, Spaceship was originally designed around Apple's private iTunes Connect API, and its migration to the official App Store Connect API has been slow. Cider has been designed with simplicity and portability in mind, which has required limiting its scope from "anything". In addition, Cider has been built around the App Store Connect API from the very beginning. What you get is a tool that is useful out-of-the-box, with simple configuration options, that runs quickly anywhere.
20 |
--------------------------------------------------------------------------------
/internal/middleware/logging.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package middleware
22 |
23 | import (
24 | "github.com/cidertool/cider/pkg/context"
25 | "github.com/fatih/color"
26 | )
27 |
28 | // Padding is a logging initial padding.
29 | type Padding int
30 |
31 | // DefaultInitialPadding is the default padding in the log library.
32 | const DefaultInitialPadding Padding = 3
33 |
34 | // ExtraPadding is the double of the DefaultInitialPadding.
35 | const ExtraPadding Padding = DefaultInitialPadding * 2
36 |
37 | // Logging pretty prints the given action and its title.
38 | // You can have different padding levels by providing different initial
39 | // paddings. The middleware will print the title in the given padding and the
40 | // action logs in padding+default padding.
41 | // The default padding in the log library is 3.
42 | // The middleware always resets to the default padding.
43 | func Logging(title string, next Action, padding Padding) Action {
44 | return func(ctx *context.Context) error {
45 | if ctx.Log == nil {
46 | return next(ctx)
47 | }
48 |
49 | defer func() {
50 | ctx.Log.SetPadding(int(DefaultInitialPadding))
51 | }()
52 |
53 | ctx.Log.SetPadding(int(padding))
54 |
55 | ctx.Log.Info(color.New(color.Bold).Sprint(title))
56 |
57 | ctx.Log.SetPadding(int(padding + DefaultInitialPadding))
58 |
59 | return next(ctx)
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/cmd/cider/main_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package main
22 |
23 | import (
24 | "testing"
25 |
26 | "github.com/stretchr/testify/assert"
27 | )
28 |
29 | func TestVersion(t *testing.T) {
30 | t.Parallel()
31 |
32 | testCases := []struct {
33 | name string
34 | version, commit, date, builtBy string
35 | out string
36 | }{
37 | {
38 | name: "all empty",
39 | out: "",
40 | },
41 | {
42 | name: "complete",
43 | version: "1.2.3",
44 | date: "12/12/12",
45 | commit: "aaaa",
46 | builtBy: "me",
47 | out: "1.2.3\ncommit: aaaa\nbuilt at: 12/12/12\nbuilt by: me",
48 | },
49 | {
50 | name: "only version",
51 | version: "1.2.3",
52 | out: "1.2.3",
53 | },
54 | {
55 | name: "version and date",
56 | version: "1.2.3",
57 | date: "12/12/12",
58 | out: "1.2.3\nbuilt at: 12/12/12",
59 | },
60 | {
61 | name: "version, date, built by",
62 | version: "1.2.3",
63 | date: "12/12/12",
64 | builtBy: "me",
65 | out: "1.2.3\nbuilt at: 12/12/12\nbuilt by: me",
66 | },
67 | }
68 |
69 | for _, tt := range testCases {
70 | tt := tt
71 |
72 | t.Run(tt.name, func(t *testing.T) {
73 | t.Parallel()
74 |
75 | assert.Equal(t, tt.out+licenseDisclaimer, buildVersion(tt.version, tt.commit, tt.date, tt.builtBy))
76 | })
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/internal/shell/shell_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package shell
22 |
23 | import (
24 | "testing"
25 |
26 | "github.com/cidertool/cider/pkg/config"
27 | "github.com/cidertool/cider/pkg/context"
28 | "github.com/stretchr/testify/assert"
29 | )
30 |
31 | func TestExec(t *testing.T) {
32 | t.Parallel()
33 |
34 | sh := New(context.New(config.Project{}))
35 | cmd := sh.NewCommand("echo", "dogs")
36 | ps, err := sh.Exec(cmd)
37 | assert.NoError(t, err)
38 | assert.Equal(t, "dogs", ps.Stdout)
39 | }
40 |
41 | func TestExec_Error(t *testing.T) {
42 | t.Parallel()
43 |
44 | sh := New(context.New(config.Project{}))
45 | cmd := sh.NewCommand("exit", "1")
46 | ps, err := sh.Exec(cmd)
47 | assert.Error(t, err)
48 | assert.NotNil(t, ps)
49 | }
50 |
51 | func TestEscapeArgs(t *testing.T) {
52 | t.Parallel()
53 |
54 | original := []string{"dan", "wears", "big jorts"}
55 | expected := []string{"dan", "wears", "'big jorts'"}
56 | actual := escapeArgs(original)
57 | assert.Equal(t, expected, actual)
58 | }
59 |
60 | func TestExists(t *testing.T) {
61 | t.Parallel()
62 |
63 | sh := New(context.New(config.Project{}))
64 | assert.True(t, sh.Exists("git"))
65 | assert.False(t, sh.Exists("nonexistent_program.exe"))
66 | }
67 |
68 | func TestCurrentDirectory(t *testing.T) {
69 | t.Parallel()
70 |
71 | ctx := context.New(config.Project{})
72 | ctx.CurrentDirectory = "TEST"
73 | sh := New(ctx)
74 | assert.Equal(t, ctx.CurrentDirectory, sh.CurrentDirectory())
75 | }
76 |
--------------------------------------------------------------------------------
/pkg/context/interrupt.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package context
22 |
23 | import (
24 | "context"
25 | "fmt"
26 | "os"
27 | "os/signal"
28 | "syscall"
29 | )
30 |
31 | type errReceivedSignal struct {
32 | Signal os.Signal
33 | }
34 |
35 | func (e errReceivedSignal) Error() string {
36 | return fmt.Sprintf("received: %s", e.Signal)
37 | }
38 |
39 | // Task is function that can be executed by an interrupt.
40 | type Task func() error
41 |
42 | // Interrupt tracks signals from the OS to determine whether to interrupt.
43 | type Interrupt struct {
44 | signals chan os.Signal
45 | errs chan error
46 | }
47 |
48 | // NewInterrupt creates an interrupt instance.
49 | func NewInterrupt() *Interrupt {
50 | return &Interrupt{
51 | signals: make(chan os.Signal, 1),
52 | errs: make(chan error, 1),
53 | }
54 | }
55 |
56 | // Run executes a given task with a given context, dealing with its timeouts,
57 | // cancels and SIGTERM and SIGINT signals.
58 | // It will return an error if the context is canceled, if deadline exceeds,
59 | // if a SIGTERM or SIGINT is received and of course if the task itself fails.
60 | func (i *Interrupt) Run(ctx context.Context, task Task) error {
61 | go func() {
62 | i.errs <- task()
63 | }()
64 | signal.Notify(i.signals, syscall.SIGINT, syscall.SIGTERM)
65 | select {
66 | case err := <-i.errs:
67 | return err
68 | case <-ctx.Done():
69 | return ctx.Err()
70 | case sig := <-i.signals:
71 | return errReceivedSignal{Signal: sig}
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/internal/parallel/group_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package parallel
22 |
23 | import (
24 | "errors"
25 | "sync"
26 | "testing"
27 | "time"
28 |
29 | "github.com/stretchr/testify/assert"
30 | )
31 |
32 | var errTestError = errors.New("TEST")
33 |
34 | func TestGroup(t *testing.T) {
35 | t.Parallel()
36 |
37 | var groupMu sync.Mutex
38 |
39 | var g = New(4)
40 |
41 | var counter int
42 |
43 | for i := 0; i < 10; i++ {
44 | g.Go(func() error {
45 | time.Sleep(10 * time.Millisecond)
46 | groupMu.Lock()
47 | counter++
48 | groupMu.Unlock()
49 |
50 | return nil
51 | })
52 | }
53 | assert.NoError(t, g.Wait())
54 | assert.Equal(t, counter, 10)
55 | }
56 |
57 | func TestGroupOrder(t *testing.T) {
58 | t.Parallel()
59 |
60 | var num = 10
61 |
62 | var g = New(1)
63 |
64 | var output = []int{}
65 |
66 | for i := 0; i < num; i++ {
67 | i := i
68 |
69 | g.Go(func() error {
70 | output = append(output, i)
71 |
72 | return nil
73 | })
74 | }
75 | assert.NoError(t, g.Wait())
76 | assert.Equal(t, []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, output)
77 | }
78 |
79 | func TestGroupOrderError(t *testing.T) {
80 | t.Parallel()
81 |
82 | var g = New(1)
83 |
84 | var output = []int{}
85 |
86 | for i := 0; i < 10; i++ {
87 | i := i
88 |
89 | g.Go(func() error {
90 | output = append(output, i)
91 |
92 | return errTestError
93 | })
94 | }
95 | assert.EqualError(t, g.Wait(), errTestError.Error())
96 | assert.Equal(t, []int{0}, output)
97 | }
98 |
--------------------------------------------------------------------------------
/internal/shell/shelltest/shelltest_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package shelltest
22 |
23 | import (
24 | "testing"
25 |
26 | "github.com/cidertool/cider/pkg/config"
27 | "github.com/cidertool/cider/pkg/context"
28 | "github.com/stretchr/testify/assert"
29 | )
30 |
31 | func TestShell(t *testing.T) {
32 | t.Parallel()
33 |
34 | ctx := context.New(config.Project{})
35 | ctx.CurrentDirectory = "TEST"
36 | sh := Shell{
37 | Context: ctx,
38 | Commands: []Command{
39 | {
40 | ReturnCode: 0,
41 | Stdout: "TEST",
42 | Stderr: "TEST",
43 | },
44 | {
45 | ReturnCode: 128,
46 | Stdout: "TEST",
47 | Stderr: "TEST",
48 | },
49 | },
50 | }
51 |
52 | dir := sh.CurrentDirectory()
53 | assert.Equal(t, dir, ctx.CurrentDirectory)
54 |
55 | exists := sh.Exists("echo")
56 | assert.True(t, exists)
57 |
58 | sh.SupportedPrograms = map[string]bool{
59 | "echo": false,
60 | }
61 | exists = sh.Exists("echo")
62 | assert.False(t, exists)
63 |
64 | cmd := sh.NewCommand("echo", "true")
65 | assert.NotNil(t, cmd)
66 |
67 | proc, err := sh.Exec(cmd)
68 | assert.NoError(t, err)
69 | assert.NotNil(t, proc)
70 |
71 | proc, err = sh.Exec(cmd)
72 | assert.EqualError(t, err, "128")
73 | assert.NotNil(t, proc)
74 |
75 | sh.expectOverflowError = true
76 | expectedErr := ErrCommandOverflow{
77 | Index: 2,
78 | Len: 2,
79 | Command: cmd.String(),
80 | }
81 | _, err = sh.Exec(cmd)
82 | assert.EqualError(t, err, expectedErr.Error())
83 | }
84 |
--------------------------------------------------------------------------------
/internal/git/errors.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package git
22 |
23 | import (
24 | "errors"
25 | "fmt"
26 | "path/filepath"
27 | )
28 |
29 | const errDirtyHeading = "git is currently in a dirty state, please check in your pipeline what can be changing the following files"
30 |
31 | // ErrDirty happens when the repo has uncommitted/unstashed changes.
32 | type ErrDirty struct {
33 | Status string
34 | }
35 |
36 | func (e ErrDirty) Error() string {
37 | return fmt.Sprintf("%s:\n%v", errDirtyHeading, e.Status)
38 | }
39 |
40 | // ErrWrongRef happens when the HEAD reference is different from the tag being built.
41 | type ErrWrongRef struct {
42 | Commit, Tag string
43 | }
44 |
45 | func (e ErrWrongRef) Error() string {
46 | return fmt.Sprintf("git tag %v was not made against commit %v", e.Tag, e.Commit)
47 | }
48 |
49 | // ErrNoTag happens if the underlying git repository doesn't contain any tags.
50 | var ErrNoTag = errors.New("git doesn't contain any tags")
51 |
52 | // ErrNotRepository happens if you try to run Cider against a folder
53 | // which is not a git repository.
54 | type ErrNotRepository struct {
55 | Dir string
56 | }
57 |
58 | func (e ErrNotRepository) Error() string {
59 | return fmt.Sprintf("the directory at %s is not a git repository", filepath.Clean(e.Dir))
60 | }
61 |
62 | // ErrNoGit happens when git is not present in PATH.
63 | var ErrNoGit = errors.New("git not present in PATH")
64 |
65 | // ErrNoRemoteOrigin happens when the repository has no remote named "origin".
66 | var ErrNoRemoteOrigin = errors.New("repository doesn't have an `origin` remote")
67 |
--------------------------------------------------------------------------------
/internal/log/log.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | // Package log is a substitute for the global apex/log that is not thread safe.
22 | package log
23 |
24 | import (
25 | "os"
26 | "sync"
27 |
28 | "github.com/apex/log"
29 | "github.com/apex/log/handlers/cli"
30 | "github.com/fatih/color"
31 | )
32 |
33 | // Interface is an extension of log.Interface.
34 | type Interface interface {
35 | log.Interface
36 | SetColorMode(v bool)
37 | SetDebug(v bool)
38 | SetPadding(v int)
39 | }
40 |
41 | // Fields re-exports log.Fields from github.com/apex/log.
42 | type Fields = log.Fields
43 |
44 | // Log is a thread-safe wrapper for log.Logger.
45 | type Log struct {
46 | log.Logger
47 | mu sync.RWMutex
48 | }
49 |
50 | // New creates a new Log instance.
51 | func New() *Log {
52 | return &Log{
53 | Logger: log.Logger{
54 | Handler: cli.New(os.Stderr),
55 | Level: log.InfoLevel,
56 | },
57 | }
58 | }
59 |
60 | // SetColorMode sets the global color mode for the logger.
61 | func (l *Log) SetColorMode(v bool) {
62 | l.mu.Lock()
63 | defer l.mu.Unlock()
64 |
65 | color.NoColor = v
66 | }
67 |
68 | // SetDebug sets the log level to Debug or Info.
69 | func (l *Log) SetDebug(v bool) {
70 | l.mu.Lock()
71 | defer l.mu.Unlock()
72 |
73 | if v {
74 | l.Level = log.DebugLevel
75 | } else {
76 | l.Level = log.InfoLevel
77 | }
78 | }
79 |
80 | // SetPadding sets the padding of the log handler in a thread-safe way.
81 | func (l *Log) SetPadding(v int) {
82 | l.mu.Lock()
83 | defer l.mu.Unlock()
84 |
85 | if handler, ok := l.Handler.(*cli.Handler); ok {
86 | handler.Padding = v
87 | l.Handler = handler
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/internal/client/assets_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package client
22 |
23 | import (
24 | "testing"
25 |
26 | "github.com/cidertool/asc-go/asc"
27 | "github.com/cidertool/cider/pkg/config"
28 | "github.com/stretchr/testify/assert"
29 | )
30 |
31 | // Test UploadRoutingCoverage
32 |
33 | func TestUploadRoutingCoverage_Happy(t *testing.T) {
34 | t.Parallel()
35 |
36 | asset := newTestAsset(t, "TEST")
37 | ctx, client := newTestContext(
38 | response{
39 | Response: asc.RoutingAppCoverageResponse{
40 | Data: asc.RoutingAppCoverage{
41 | Attributes: &asc.RoutingAppCoverageAttributes{
42 | UploadOperations: []asc.UploadOperation{},
43 | },
44 | },
45 | },
46 | },
47 | response{
48 | Response: asc.RoutingAppCoverageResponse{
49 | Data: asc.RoutingAppCoverage{
50 | Attributes: &asc.RoutingAppCoverageAttributes{
51 | UploadOperations: []asc.UploadOperation{},
52 | },
53 | },
54 | },
55 | },
56 | response{
57 | Response: asc.RoutingAppCoverageResponse{
58 | Data: asc.RoutingAppCoverage{
59 | Attributes: &asc.RoutingAppCoverageAttributes{
60 | UploadOperations: []asc.UploadOperation{},
61 | },
62 | },
63 | },
64 | },
65 | response{
66 | Response: asc.RoutingAppCoverageResponse{
67 | Data: asc.RoutingAppCoverage{
68 | Attributes: &asc.RoutingAppCoverageAttributes{
69 | UploadOperations: []asc.UploadOperation{},
70 | },
71 | },
72 | },
73 | },
74 | )
75 |
76 | defer ctx.Close()
77 |
78 | err := client.UploadRoutingCoverage(ctx.Context, "TEST", config.File{
79 | Path: asset.Name,
80 | })
81 | assert.NoError(t, err)
82 | }
83 |
--------------------------------------------------------------------------------
/internal/clicommand/check.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package clicommand
22 |
23 | import (
24 | "fmt"
25 |
26 | "github.com/cidertool/cider/internal/pipe/defaults"
27 | "github.com/cidertool/cider/pkg/context"
28 | "github.com/fatih/color"
29 | "github.com/spf13/cobra"
30 | )
31 |
32 | type checkCmd struct {
33 | cmd *cobra.Command
34 | debugFlagValue *bool
35 | config string
36 | }
37 |
38 | func newCheckCmd(debugFlagValue *bool) *checkCmd {
39 | var root = &checkCmd{debugFlagValue: debugFlagValue}
40 |
41 | var cmd = &cobra.Command{
42 | Use: "check",
43 | Short: "Checks if the configuration is valid",
44 | Long: `Use to validate your configuration file.`,
45 | Example: "cider check",
46 | SilenceUsage: true,
47 | SilenceErrors: true,
48 | RunE: root.Run,
49 | }
50 |
51 | cmd.Flags().StringVarP(&root.config, "config", "f", "", "Configuration file to check")
52 |
53 | root.cmd = cmd
54 |
55 | return root
56 | }
57 |
58 | func (cmd *checkCmd) Run(c *cobra.Command, args []string) error {
59 | logger := newLogger(cmd.debugFlagValue)
60 |
61 | cfg, err := loadConfig(cmd.config, "")
62 | if err != nil {
63 | return err
64 | }
65 |
66 | var ctx = context.New(cfg)
67 |
68 | if err := context.NewInterrupt().Run(ctx, func() error {
69 | logger.Info(color.New(color.Bold).Sprint("checking config:"))
70 |
71 | return defaults.Pipe{}.Run(ctx)
72 | }); err != nil {
73 | logger.WithError(err).Error(color.New(color.Bold).Sprintf("config is invalid"))
74 |
75 | return fmt.Errorf("invalid config: %w", err)
76 | }
77 |
78 | logger.Info(color.New(color.Bold).Sprintf("config is valid"))
79 |
80 | return nil
81 | }
82 |
--------------------------------------------------------------------------------
/internal/parallel/group.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | // Package parallel wraps a error group with a semaphore with configurable
22 | // size, so you can control the number of tasks being executed simultaneously.
23 | package parallel
24 |
25 | import (
26 | "sync"
27 |
28 | "golang.org/x/sync/errgroup"
29 | )
30 |
31 | // Group is the Semphore ErrorGroup itself.
32 | type Group interface {
33 | Go(func() error)
34 | Wait() error
35 | }
36 |
37 | // New returns a new Group of a given size.
38 | func New(size int) Group {
39 | if size <= 1 {
40 | return &serialGroup{}
41 | }
42 |
43 | return ¶llelGroup{
44 | ch: make(chan bool, size),
45 | g: errgroup.Group{},
46 | }
47 | }
48 |
49 | type parallelGroup struct {
50 | ch chan bool
51 | g errgroup.Group
52 | }
53 |
54 | // Go execs one function respecting the group and semaphore.
55 | func (s *parallelGroup) Go(fn func() error) {
56 | s.g.Go(func() error {
57 | s.ch <- true
58 | defer func() {
59 | <-s.ch
60 | }()
61 |
62 | return fn()
63 | })
64 | }
65 |
66 | // Wait waits for the group to complete and return an error if any.
67 | func (s *parallelGroup) Wait() error {
68 | return s.g.Wait()
69 | }
70 |
71 | type serialGroup struct {
72 | err error
73 | errOnce sync.Once
74 | }
75 |
76 | // Go execs runs `fn` and saves the result if no error has been encountered.
77 | func (s *serialGroup) Go(fn func() error) {
78 | if s.err != nil {
79 | return
80 | }
81 |
82 | if err := fn(); err != nil {
83 | s.errOnce.Do(func() {
84 | s.err = err
85 | })
86 | }
87 | }
88 |
89 | // Wait waits for Go to complete and returns the first error encountered.
90 | func (s *serialGroup) Wait() error {
91 | return s.err
92 | }
93 |
--------------------------------------------------------------------------------
/pkg/context/context_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package context
22 |
23 | import (
24 | "testing"
25 | "time"
26 |
27 | "github.com/cidertool/cider/pkg/config"
28 | "github.com/stretchr/testify/assert"
29 | )
30 |
31 | func TestNew(t *testing.T) {
32 | t.Parallel()
33 |
34 | ctx := New(config.Project{})
35 | assert.Equal(t, 1, ctx.MaxProcesses)
36 | }
37 |
38 | func TestNewWithTimeout(t *testing.T) {
39 | t.Parallel()
40 |
41 | ctx, cancel := NewWithTimeout(config.Project{}, time.Second)
42 | assert.NotEmpty(t, ctx.Env)
43 | cancel()
44 | <-ctx.Done()
45 | assert.EqualError(t, ctx.Err(), `context canceled`)
46 | }
47 |
48 | func TestEnv(t *testing.T) {
49 | t.Parallel()
50 |
51 | var env = Env{"DOG": "FRIEND"}
52 | anotherEnv := env.Copy()
53 | assert.Equal(t, env, anotherEnv)
54 | assert.NotSame(t, &env, &anotherEnv)
55 | assert.Equal(t, []string{"DOG=FRIEND"}, env.Strings())
56 | }
57 |
58 | func TestPublishMode(t *testing.T) {
59 | t.Parallel()
60 |
61 | var mode PublishMode
62 | mode = PublishModeAppStore
63 | assert.Equal(t, "appstore", mode.String())
64 | assert.Equal(t, "{appstore,testflight}", mode.Type())
65 | mode = PublishModeTestflight
66 | assert.Equal(t, "testflight", mode.String())
67 | assert.Equal(t, "{appstore,testflight}", mode.Type())
68 | mode = PublishMode("bad")
69 | assert.Equal(t, "bad", mode.String())
70 | assert.Equal(t, "{appstore,testflight}", mode.Type())
71 |
72 | var err error
73 | err = mode.Set("appstore")
74 | assert.NoError(t, err)
75 | assert.Equal(t, PublishModeAppStore, mode)
76 | err = mode.Set("testflight")
77 | assert.NoError(t, err)
78 | assert.Equal(t, PublishModeTestflight, mode)
79 | err = mode.Set("bad")
80 | assert.Error(t, err)
81 | }
82 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
7 |
Submit to the App Store in seconds!
8 |
9 |
10 | ---
11 |
12 | Cider is a tool managing the entire release process of an iOS, macOS or tvOS application, supported by official Apple APIs. It takes the builds you've uploaded to App Store Connect, updates their metadata, and submits them for review automatically using an expressive YAML configuration. Unlike Xcode or altool, Cider is designed to be useful on Linux and Windows, in addition to macOS.
13 |
14 | ## Documentation
15 |
16 | Documentation is hosted at . Check out our installation and quick start documentation!
17 |
18 | ## Integrations
19 |
20 | - [GitHub Action](https://github.com/marketplace/actions/cider-action)
21 | - [Buildkite Plugin](https://github.com/cidertool/cider-buildkite-plugin)
22 |
23 | ## Badges
24 |
25 | 
26 | [](https://codecov.io/gh/cidertool/cider)
27 | [](/COPYING)
28 | [](https://github.com/cidertool/cider/releases/latest)
29 | [](https://hub.docker.com/r/cidertool/cider)
30 | [](https://somsubhra.com/github-release-stats/?username=cidertool&repository=cider)
31 |
32 | ## Contributing
33 |
34 | This project's primary goal is to simplify the process to release on the App Store, and enable the entire build + test + release process to be executable in the command line. Until the package's version stabilizes with v1, there isn't a strong roadmap beyond those stated goals. However, contributions are always welcome. If you want to get involved or you just want to offer feedback, please see [`CONTRIBUTING.md`](https://github.com/cidertool/.github/blob/main/CONTRIBUTING.md) for details.
35 |
36 | ## Credits
37 |
38 | Special thanks to:
39 |
40 | - [GoReleaser](https://goreleaser.com/) for inspiring the architecture and open sourcing several components used in Cider
41 |
42 | ## License
43 |
44 | This library is licensed under the GNU General Public License v3.0 or later
45 |
46 | See [COPYING](./COPYING) to see the full text.
47 |
--------------------------------------------------------------------------------
/docs/configuration-footer.md:
--------------------------------------------------------------------------------
1 | ## Locales
2 |
3 | The App Store operates in a variety of locales and territories. When referring to localized resources in Cider such as [AppLocalizations](#applocalizations), [VersionLocalizations](#versionlocalizations), or [TestflightLocalizations](#testflightlocalizations), use ISO 639-1 identifiers where possible, in the style of `"en-US"` where possible. If an ISO 639-1 code does not exist, use the appropriate ISO 639-2 code.
4 |
5 | ## App Categories
6 |
7 | App categories provided and supported by the App Store Connect API are fluid and difficult to create a consistent format for. The App Store adds categories regularly, and it represents a challenge for both metadata maintainers and maintainers of Cider to support. Therefore, the choice has been made to accept any string as a category ID, and let the API respond with whether or not it's valid.
8 |
9 | Here are some known category IDs, with subcategories broken out where applicable, that you can use in your configuration:
10 |
11 | - `"BOOKS"`
12 | - `"BUSINESS"`
13 | - `"DEVELOPER_TOOLS"`
14 | - `"EDUCATION"`
15 | - `"ENTERTAINMENT"`
16 | - `"FINANCE"`
17 | - `"FOOD_AND_DRINK"`
18 | - `"GAMES"`
19 | - `"GAMES_SPORTS"`
20 | - `"GAMES_WORD"`
21 | - `"GAMES_MUSIC"`
22 | - `"GAMES_ADVENTURE"`
23 | - `"GAMES_ACTION"`
24 | - `"GAMES_ROLE_PLAYING"`
25 | - `"GAMES_CASUAL"`
26 | - `"GAMES_BOARD"`
27 | - `"GAMES_TRIVIA"`
28 | - `"GAMES_CARD"`
29 | - `"GAMES_PUZZLE"`
30 | - `"GAMES_CASINO"`
31 | - `"GAMES_STRATEGY"`
32 | - `"GAMES_SIMULATION"`
33 | - `"GAMES_RACING"`
34 | - `"GAMES_FAMILY"`
35 | - `"HEALTH_AND_FITNESS"`
36 | - `"LIFESTYLE"`
37 | - `"MAGAZINES_AND_NEWSPAPERS"`
38 | - `"MEDICAL"`
39 | - `"PRODUCTIVITY"`
40 | - `"REFERENCE"`
41 | - `"SHOPPING"`
42 | - `"SOCIAL_NETWORKING"`
43 | - `"SPORTS"`
44 | - `"STICKERS"`
45 | - `"STICKERS_PLACES_AND_OBJECTS"`
46 | - `"STICKERS_EMOJI_AND_EXPRESSIONS"`
47 | - `"STICKERS_CELEBRATIONS"`
48 | - `"STICKERS_CELEBRITIES"`
49 | - `"STICKERS_MOVIES_AND_TV"`
50 | - `"STICKERS_SPORTS_AND_ACTIVITIES"`
51 | - `"STICKERS_EATING_AND_DRINKING"`
52 | - `"STICKERS_CHARACTERS"`
53 | - `"STICKERS_ANIMALS"`
54 | - `"STICKERS_FASHION"`
55 | - `"STICKERS_ART"`
56 | - `"STICKERS_GAMING"`
57 | - `"STICKERS_KIDS_AND_FAMILY"`
58 | - `"STICKERS_PEOPLE"`
59 | - `"STICKERS_MUSIC"`
60 | - `"MUSIC"`
61 | - `"TRAVEL"`
62 | - `"UTILITIES"`
63 | - `"WEATHER"`
64 |
65 | For more information on categories, see [Choosing a category](https://developer.apple.com/app-store/categories/) on the Apple Developer Portal.
66 |
--------------------------------------------------------------------------------
/internal/pipe/pipe.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | // Package pipe declares utilities and errors for pipes
22 | package pipe
23 |
24 | import (
25 | "errors"
26 | "fmt"
27 | )
28 |
29 | // ErrSkipGitEnabled happens if --skip-git is set. It means that the part of a Piper that
30 | // extracts metadata from the Git repository was not run.
31 | var ErrSkipGitEnabled = Skip("inspecting git state is disabled")
32 |
33 | // ErrSkipNoAppsToPublish happens when there are no apps in the configuration to publish
34 | // or update metadata for. It will be raised when the configuration is effectively empty.
35 | var ErrSkipNoAppsToPublish = Skip("no apps selected to publish")
36 |
37 | // ErrSkipSubmitEnabled happens if --skip-submit is set.
38 | // It means that the part of a Piper that submits to Apple for review was not run.
39 | var ErrSkipSubmitEnabled = Skip("submission is disabled")
40 |
41 | // ErrMissingApp happens when an app is selected in the interface that is not defined in the configuration.
42 | type ErrMissingApp struct {
43 | Name string
44 | }
45 |
46 | func (e ErrMissingApp) Error() string {
47 | return fmt.Sprintf("no app defined in configuration matching the name %s", e.Name)
48 | }
49 |
50 | // IsSkip returns true if the error is an ErrSkip.
51 | func IsSkip(err error) bool {
52 | var serr ErrSkip
53 | ok := errors.As(err, &serr)
54 |
55 | return ok
56 | }
57 |
58 | // ErrSkip occurs when a pipe is skipped for some reason.
59 | type ErrSkip struct {
60 | reason string
61 | }
62 |
63 | // Error implements the error interface. returns the reason the pipe was skipped.
64 | func (e ErrSkip) Error() string {
65 | return e.reason
66 | }
67 |
68 | // Skip skips this pipe with the given reason.
69 | func Skip(reason string) ErrSkip {
70 | return ErrSkip{reason: reason}
71 | }
72 |
--------------------------------------------------------------------------------
/docs/quick-start.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | nav_order: 3
4 | ---
5 |
6 | # Quick Start
7 |
8 | Once you've [installed](./install.md) Cider, you can get started setting it up for your project.
9 |
10 | Run `cider init` to create a new `.cider.yml` file in the current directory:
11 |
12 | ```shell
13 | cider init
14 | ```
15 |
16 | This will run through a series of prompts where you will get to set some default values for your project. See [configuration.md](./configuration.md) for additional options and documentation on the entire project specification. This file should be checked in to source control.
17 |
18 | Once this file is set up, you can either proceed to run `cider` [locally](#local), or set it up in [CI](#ci).
19 |
20 | ## Local
21 |
22 | The most simple invocation of Cider to submit an app is as follows:
23 |
24 | ```
25 | cider release --mode appstore
26 | ```
27 |
28 | Cider contains a host of options enabling you to customize its runtime. Follow the guide on the [`release` command](./commands/cider_release.md).
29 |
30 | ## CI
31 |
32 | ### GitHub Actions
33 |
34 | Cider can also be run autonomously using the official [Cider Action](https://github.com/marketplace/actions/cider-action) hosted on the GitHub Marketplace. The Action is versioned independently of Cider, and all of Cider's commands and internal capabilities are available.
35 |
36 | #### Usage
37 |
38 | ```yaml
39 | - uses: actions/checkout@v2
40 | - uses: cidertool/cider-action@v0
41 | with:
42 | version: latest
43 | args: release --mode appstore --set-version ${{ env.VERSION }}
44 | env:
45 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
46 | ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
47 | ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
48 | ASC_PRIVATE_KEY: ${{ secrets.ASC_PRIVATE_KEY }}
49 | ```
50 |
51 | You can run this job in any context you see fit to use Cider to update app metadata or submit new versions of your apps to the App Store.
52 |
53 | ### Buildkite
54 |
55 | If you're using Buildkite, you can use the [Cider Buildkite Plugin](https://github.com/cidertool/cider-buildkite-plugin). Similarly to the GitHub Action, the plugin is versioned independently of Cider and any function available in the Cider command line can be used. This plugin requires Docker.
56 |
57 | #### Usage
58 |
59 | ```yaml
60 | steps:
61 | - label: ':apple: Release with Cider'
62 | plugins:
63 | - cidertool/cider#v0.1.0:
64 | args: release --mode appstore
65 | env:
66 | ASC_KEY_ID: '...'
67 | ASC_ISSUER_ID: '...'
68 | ASC_PRIVATE_KEY: '...'
69 | ```
70 |
--------------------------------------------------------------------------------
/pkg/config/testdata/valid.yml:
--------------------------------------------------------------------------------
1 | ---
2 | Wayfair:
3 | id: com.sky.ProjectApp
4 | localizations:
5 | en-US:
6 | name: My App
7 | subtitle: congratulations
8 | privacyPolicyText: go away
9 | privacyPolicyURL: https://google.com
10 | ja:
11 | name: 僕のアップ
12 | subtitle: おめでとう
13 | privacyPolicyText: 消えろ
14 | privacyPolicyURL: https://google.co.jp
15 | versions:
16 | platform: iOS
17 | copyright: 2020 Wayfair LLC
18 | earliestReleaseDate: 2020-08-07T14:25:00Z
19 | releaseType: afterApproval
20 | enablePhasedRelease: true
21 | localizations:
22 | en-US:
23 | description: ''
24 | keywords: ''
25 | marketingURL: https://google.com
26 | promotionalText: ''
27 | supportURL: https://google.com
28 | whatsNew: ''
29 | previewSets:
30 | iphone65:
31 | - path: ''
32 | mimeType: ''
33 | previewFrameTimeCode: ''
34 | screenshotSets:
35 | iphone65:
36 | - path: ''
37 | idfaDeclaration:
38 | attributesActionWithPreviousAd: true
39 | attributesAppInstallationToPreviousAd: true
40 | honorsLimitedAdTracking: true
41 | servesAds: true
42 | routingCoverage:
43 | path: ''
44 | reviewDetails:
45 | contact:
46 | email: ''
47 | firstName: ''
48 | lastName: ''
49 | phone: ''
50 | demoAccount:
51 | name: ''
52 | password: ''
53 | isRequired: false
54 | notes: ''
55 | attachments:
56 | - path: ''
57 | testflight:
58 | enableAutoNotify: false
59 | licenseAgreement: ''
60 | betaGroups:
61 | - group: 'My Colleagues'
62 | testers:
63 | - email: 'jeff@mail.com'
64 | - email: 'geoff@mail.com'
65 | - group: 'My Friends'
66 | testers:
67 | - email: 'jeff@mail.com'
68 | - email: 'geoff@mail.com'
69 | betaTesters:
70 | - email: 'jeff@mail.com'
71 | - email: 'geoff@mail.com'
72 | localizations:
73 | en-US:
74 | description: ''
75 | feedbackEmail: ''
76 | marketingURL: https://google.com
77 | privacyPolicyURL: https://google.com
78 | tvOSPrivacyPolicy: ''
79 | whatsNew: ''
80 | reviewDetails:
81 | contact:
82 | email: ''
83 | firstName: ''
84 | lastName: ''
85 | phone: ''
86 | demoAccount:
87 | name: ''
88 | password: ''
89 | isRequired: false
90 | notes: ''
91 |
--------------------------------------------------------------------------------
/internal/pipe/testflight/testflight_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package testflight
22 |
23 | import (
24 | "testing"
25 |
26 | "github.com/cidertool/cider/internal/client/clienttest"
27 | "github.com/cidertool/cider/internal/pipe"
28 | "github.com/cidertool/cider/pkg/config"
29 | "github.com/cidertool/cider/pkg/context"
30 | "github.com/stretchr/testify/assert"
31 | )
32 |
33 | func TestTestflight_Happy(t *testing.T) {
34 | t.Parallel()
35 |
36 | ctx := context.New(config.Project{
37 | "TEST": {
38 | BundleID: "com.test.TEST",
39 | Testflight: config.Testflight{
40 | ReviewDetails: &config.ReviewDetails{
41 | Contact: &config.ContactPerson{
42 | Email: "test@example.com",
43 | FirstName: "Person",
44 | LastName: "Personson",
45 | Phone: "1555555555",
46 | },
47 | DemoAccount: &config.DemoAccount{},
48 | Notes: "TEST",
49 | Attachments: []config.File{
50 | {Path: "TEST"},
51 | },
52 | },
53 | },
54 | },
55 | })
56 | ctx.AppsToRelease = []string{"TEST"}
57 |
58 | p := Pipe{}
59 | p.Client = &clienttest.Client{}
60 |
61 | assert.Equal(t, "committing to testflight", p.String())
62 |
63 | err := p.Publish(ctx)
64 | assert.NoError(t, err)
65 | }
66 |
67 | func TestTestflight_Happy_Skips(t *testing.T) {
68 | t.Parallel()
69 |
70 | ctx := context.New(config.Project{
71 | "TEST": {
72 | BundleID: "com.test.TEST",
73 | },
74 | })
75 | ctx.AppsToRelease = []string{"TEST"}
76 | ctx.SkipUpdateMetadata = true
77 | ctx.SkipSubmit = true
78 |
79 | p := Pipe{}
80 | p.Client = &clienttest.Client{}
81 |
82 | err := p.Publish(ctx)
83 | assert.EqualError(t, err, pipe.ErrSkipSubmitEnabled.Error())
84 | }
85 |
86 | func TestTestflight_Happy_NoApps(t *testing.T) {
87 | t.Parallel()
88 |
89 | ctx := context.New(config.Project{})
90 | ctx.Credentials = &clienttest.Credentials{}
91 |
92 | p := Pipe{}
93 |
94 | err := p.Publish(ctx)
95 | assert.NoError(t, err)
96 | }
97 |
--------------------------------------------------------------------------------
/internal/clicommand/root.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package clicommand
22 |
23 | import (
24 | "errors"
25 | "fmt"
26 |
27 | "github.com/spf13/cobra"
28 | )
29 |
30 | // Execute is the primary function to initiate the command line interface for Cider.
31 | func Execute(version string, exit func(int), args []string) {
32 | // nolint: forbidigo
33 | fmt.Println()
34 | // nolint: forbidigo
35 | defer fmt.Println()
36 |
37 | NewRoot(version, exit).Execute(args)
38 | }
39 |
40 | // Root defines a rough structure for a root command type.
41 | type Root struct {
42 | Cmd *cobra.Command
43 | exit func(int)
44 | }
45 |
46 | // NewRoot creates a new instance of the root command for the cider executable.
47 | func NewRoot(version string, exit func(int)) *Root {
48 | var root = &Root{
49 | exit: exit,
50 | }
51 |
52 | var debug bool
53 |
54 | var cmd = &cobra.Command{
55 | Use: "cider",
56 | Short: "Submit your builds to the Apple App Store in seconds",
57 | Version: version,
58 | SilenceUsage: true,
59 | SilenceErrors: true,
60 | DisableAutoGenTag: true,
61 | }
62 |
63 | cmd.PersistentFlags().BoolVar(&debug, "debug", false, "Enable debug mode")
64 |
65 | cmd.AddCommand(
66 | newInitCmd(&debug).cmd,
67 | newCheckCmd(&debug).cmd,
68 | newReleaseCmd(&debug).cmd,
69 | newCompletionsCmd().cmd,
70 | )
71 |
72 | root.Cmd = cmd
73 |
74 | return root
75 | }
76 |
77 | // Execute executes the root command.
78 | func (cmd *Root) Execute(args []string) {
79 | cmd.Cmd.SetArgs(args)
80 |
81 | if err := cmd.Cmd.Execute(); err != nil {
82 | var code = 1
83 |
84 | var msg = "command failed"
85 |
86 | var eerr *exitError
87 |
88 | if ok := errors.As(err, &eerr); ok {
89 | code = eerr.code
90 |
91 | if eerr.details != "" {
92 | msg = eerr.details
93 | }
94 | }
95 |
96 | newLogger(nil).WithError(err).Error(msg)
97 |
98 | cmd.exit(code)
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/internal/pipe/publish/publish.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | // Package publish is a pipe that runs the testflight or store pipes depending on publish mode
22 | package publish
23 |
24 | import (
25 | "fmt"
26 |
27 | "github.com/cidertool/cider/internal/client"
28 | "github.com/cidertool/cider/internal/middleware"
29 | "github.com/cidertool/cider/internal/pipe"
30 | "github.com/cidertool/cider/internal/pipe/store"
31 | "github.com/cidertool/cider/internal/pipe/testflight"
32 | "github.com/cidertool/cider/pkg/context"
33 | )
34 |
35 | // errUnsupportedPublishMode happens when an unsupported publish mode is provided to the pipe.
36 | type errUnsupportedPublishMode struct {
37 | mode context.PublishMode
38 | }
39 |
40 | func (e errUnsupportedPublishMode) Error() string {
41 | return fmt.Sprintf("failed to publish: unsupported publish mode %s", e.mode)
42 | }
43 |
44 | // Pipe that publishes artifacts.
45 | type Pipe struct {
46 | client client.Client
47 | }
48 |
49 | func (Pipe) String() string {
50 | return "publishing from app store connect"
51 | }
52 |
53 | // Publisher should be implemented by pipes that want to publish artifacts.
54 | type Publisher interface {
55 | fmt.Stringer
56 |
57 | // Default sets the configuration defaults
58 | Publish(ctx *context.Context) error
59 | }
60 |
61 | // Run the pipe.
62 | func (p Pipe) Run(ctx *context.Context) error {
63 | if len(ctx.AppsToRelease) == 0 {
64 | return pipe.ErrSkipNoAppsToPublish
65 | }
66 |
67 | var publisher Publisher
68 |
69 | switch ctx.PublishMode {
70 | case context.PublishModeTestflight:
71 | publisher = &testflight.Pipe{Client: p.client}
72 | case context.PublishModeAppStore:
73 | publisher = &store.Pipe{Client: p.client}
74 | default:
75 | return errUnsupportedPublishMode{ctx.PublishMode}
76 | }
77 |
78 | if err := middleware.Logging(
79 | publisher.String(),
80 | middleware.ErrHandler(publisher.Publish),
81 | middleware.ExtraPadding,
82 | )(ctx); err != nil {
83 | return fmt.Errorf("%s: failed to publish: %w", publisher.String(), err)
84 | }
85 |
86 | return nil
87 | }
88 |
--------------------------------------------------------------------------------
/internal/pipe/env/env.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | // Package env is a pipe that loads environment variables
22 | package env
23 |
24 | import (
25 | "errors"
26 | "fmt"
27 | "io"
28 | "os"
29 | "path/filepath"
30 |
31 | "github.com/cidertool/cider/pkg/context"
32 | )
33 |
34 | // ErrMissingEnvVar indicates an error when a required variable is missing in the environment.
35 | var ErrMissingEnvVar = errors.New("missing required environment variable")
36 |
37 | // Pipe is a global hook pipe.
38 | type Pipe struct{}
39 |
40 | // String is the name of this pipe.
41 | func (Pipe) String() string {
42 | return "loading environment variables"
43 | }
44 |
45 | // Run executes the hooks.
46 | func (p Pipe) Run(ctx *context.Context) error {
47 | keyID, err := loadEnv("ASC_KEY_ID", true)
48 | if err != nil {
49 | return err
50 | }
51 |
52 | issuerID, err := loadEnv("ASC_ISSUER_ID", true)
53 |
54 | if err != nil {
55 | return err
56 | }
57 |
58 | privateKey, err := loadEnv("ASC_PRIVATE_KEY", true)
59 |
60 | if err != nil {
61 | privateKey, err = loadEnvFromPath("ASC_PRIVATE_KEY_PATH", true)
62 | if err != nil {
63 | return err
64 | }
65 | }
66 |
67 | creds, err := context.NewCredentials(keyID, issuerID, []byte(privateKey))
68 |
69 | if err != nil {
70 | return err
71 | }
72 |
73 | ctx.Credentials = creds
74 |
75 | return nil
76 | }
77 |
78 | func loadEnv(env string, required bool) (string, error) {
79 | val := os.Getenv(env)
80 | if val == "" && required {
81 | return "", fmt.Errorf("key %s not found: %w", env, ErrMissingEnvVar)
82 | }
83 |
84 | return val, nil
85 | }
86 |
87 | func loadEnvFromPath(env string, required bool) (string, error) {
88 | val, err := loadEnv(env, required)
89 | if err != nil {
90 | return "", err
91 | }
92 |
93 | f, err := os.Open(filepath.Clean(val))
94 |
95 | if err != nil {
96 | if required {
97 | return "", err
98 | }
99 |
100 | return "", nil
101 | }
102 |
103 | bytes, err := io.ReadAll(f)
104 |
105 | return string(bytes), err
106 | }
107 |
--------------------------------------------------------------------------------
/internal/pipe/store/store_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package store
22 |
23 | import (
24 | "testing"
25 |
26 | "github.com/cidertool/cider/internal/client/clienttest"
27 | "github.com/cidertool/cider/internal/pipe"
28 | "github.com/cidertool/cider/pkg/config"
29 | "github.com/cidertool/cider/pkg/context"
30 | "github.com/stretchr/testify/assert"
31 | )
32 |
33 | func TestStore_Happy(t *testing.T) {
34 | t.Parallel()
35 |
36 | ctx := context.New(config.Project{
37 | "TEST": {
38 | BundleID: "com.test.TEST",
39 | Versions: config.Version{
40 | PhasedReleaseEnabled: true,
41 | IDFADeclaration: &config.IDFADeclaration{
42 | HonorsLimitedAdTracking: true,
43 | },
44 | RoutingCoverage: &config.File{
45 | Path: "TEST",
46 | },
47 | ReviewDetails: &config.ReviewDetails{
48 | Contact: &config.ContactPerson{
49 | Email: "test@example.com",
50 | FirstName: "Person",
51 | LastName: "Personson",
52 | Phone: "1555555555",
53 | },
54 | DemoAccount: &config.DemoAccount{},
55 | Notes: "TEST",
56 | Attachments: []config.File{
57 | {Path: "TEST"},
58 | },
59 | },
60 | },
61 | },
62 | })
63 | ctx.AppsToRelease = []string{"TEST"}
64 |
65 | p := Pipe{}
66 | p.Client = &clienttest.Client{}
67 |
68 | assert.Equal(t, "committing to app store", p.String())
69 |
70 | err := p.Publish(ctx)
71 | assert.NoError(t, err)
72 | }
73 |
74 | func TestStore_Happy_Skips(t *testing.T) {
75 | t.Parallel()
76 |
77 | ctx := context.New(config.Project{
78 | "TEST": {
79 | BundleID: "com.test.TEST",
80 | },
81 | })
82 | ctx.AppsToRelease = []string{"TEST"}
83 | ctx.SkipUpdatePricing = true
84 | ctx.SkipUpdateMetadata = true
85 | ctx.SkipSubmit = true
86 |
87 | p := Pipe{}
88 | p.Client = &clienttest.Client{}
89 |
90 | err := p.Publish(ctx)
91 | assert.EqualError(t, err, pipe.ErrSkipSubmitEnabled.Error())
92 | }
93 |
94 | func TestStore_Happy_NoApps(t *testing.T) {
95 | t.Parallel()
96 |
97 | ctx := context.New(config.Project{})
98 | ctx.Credentials = &clienttest.Credentials{}
99 |
100 | p := Pipe{}
101 |
102 | err := p.Publish(ctx)
103 | assert.NoError(t, err)
104 | }
105 |
--------------------------------------------------------------------------------
/tools/gendoc/md.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package main
22 |
23 | import (
24 | "fmt"
25 | "os"
26 | "path/filepath"
27 | "strings"
28 |
29 | "github.com/apex/log"
30 | "github.com/cidertool/cider/internal/clicommand"
31 | "github.com/spf13/cobra"
32 | "github.com/spf13/cobra/doc"
33 | )
34 |
35 | const docsMdFrontmatterTemplate = `---
36 | layout: page
37 | parent: Commands
38 | title: %s
39 | nav_order: %d
40 | nav_exclude: %t
41 | ---
42 |
43 | `
44 |
45 | type pageNavField struct {
46 | order int
47 | exclude bool
48 | }
49 |
50 | func runDocsMdCmd(cmd *cobra.Command, args []string) error {
51 | var orderRoot, orderInit, orderRelease, orderCheck, orderCompletions = 0, 1, 2, 3, 4
52 |
53 | var pageNavFields = map[string]pageNavField{
54 | "cider.md": {order: orderRoot},
55 | "cider_init.md": {order: orderInit},
56 | "cider_release.md": {order: orderRelease},
57 | "cider_check.md": {order: orderCheck},
58 | "cider_completions.md": {order: orderCompletions},
59 | }
60 |
61 | var dir string
62 | if len(args) == 0 {
63 | dir = defaultDocsPath
64 | } else {
65 | dir = args[0]
66 | }
67 |
68 | dir = filepath.Join(dir, "commands")
69 |
70 | prepender := func(filename string) string {
71 | base := filepath.Base(filename)
72 |
73 | return fmt.Sprintf(docsMdFrontmatterTemplate, pageTitle(base), pageNavFields[base].order, pageNavFields[base].exclude)
74 | }
75 |
76 | linkHandler := func(name string) string {
77 | base := strings.TrimSuffix(name, filepath.Ext(name))
78 |
79 | return "/commands/" + strings.ToLower(base) + "/"
80 | }
81 |
82 | log.WithField("path", dir).Info("generating Markdown documentation")
83 |
84 | err := doc.GenMarkdownTreeCustom(clicommand.NewRoot("dev", os.Exit).Cmd, dir, prepender, linkHandler)
85 | if err != nil {
86 | log.Error("generation failed")
87 | } else {
88 | log.Info("generation completed successfully")
89 | }
90 |
91 | return err
92 | }
93 |
94 | func pageTitle(s string) string {
95 | s = strings.TrimSuffix(s, filepath.Ext(s))
96 | if s != "cider" {
97 | s = strings.ReplaceAll(s, "cider", "")
98 | }
99 |
100 | s = strings.ReplaceAll(s, "_", " ")
101 | s = strings.TrimSpace(s)
102 |
103 | return s
104 | }
105 |
--------------------------------------------------------------------------------
/tools/licensing/main.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package main
22 |
23 | import (
24 | "os"
25 | "path/filepath"
26 | "strings"
27 |
28 | "github.com/apex/log"
29 | "github.com/spf13/cobra"
30 | )
31 |
32 | const licenseHeader = `/**
33 | Copyright (C) 2020 Aaron Sky.
34 |
35 | This file is part of Cider, a tool for automating submission
36 | of apps to Apple's App Stores.
37 |
38 | Cider is free software: you can redistribute it and/or modify
39 | it under the terms of the GNU General Public License as published by
40 | the Free Software Foundation, either version 3 of the License, or
41 | (at your option) any later version.
42 |
43 | Cider is distributed in the hope that it will be useful,
44 | but WITHOUT ANY WARRANTY; without even the implied warranty of
45 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
46 | GNU General Public License for more details.
47 |
48 | You should have received a copy of the GNU General Public License
49 | along with Cider. If not, see .
50 | */`
51 |
52 | func main() {
53 | var cmd = &cobra.Command{
54 | Use: "licensing",
55 | Short: "Ensure every source file in the repo contains a license header",
56 | Args: cobra.MaximumNArgs(1),
57 | DisableAutoGenTag: true,
58 | RunE: runLicensing,
59 | }
60 |
61 | cmd.SetArgs(os.Args[1:])
62 |
63 | if err := cmd.Execute(); err != nil {
64 | var code = 1
65 |
66 | var msg = "command failed"
67 |
68 | log.WithError(err).Error(msg)
69 |
70 | os.Exit(code)
71 | }
72 | }
73 |
74 | func runLicensing(cmd *cobra.Command, args []string) error {
75 | return filepath.Walk(".", checkFile)
76 | }
77 |
78 | func checkFile(path string, info os.FileInfo, err error) error {
79 | if err != nil {
80 | return err
81 | }
82 |
83 | if filepath.Ext(path) != ".go" {
84 | return nil
85 | }
86 |
87 | f, err := os.ReadFile(path) // #nosec
88 | if err != nil {
89 | return err
90 | }
91 |
92 | source := string(f)
93 |
94 | if !strings.HasPrefix(source, licenseHeader) {
95 | source = licenseHeader + "\n\n" + source
96 |
97 | err := os.WriteFile(path, []byte(source), info.Mode())
98 | if err != nil {
99 | return err
100 | }
101 |
102 | log.Info(path)
103 | }
104 |
105 | return nil
106 | }
107 |
--------------------------------------------------------------------------------
/tools/gendoc/main.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package main
22 |
23 | import (
24 | "os"
25 |
26 | "github.com/apex/log"
27 | "github.com/spf13/cobra"
28 | )
29 |
30 | const defaultDocsPath = "docs"
31 |
32 | type rootCmd struct {
33 | cmd *cobra.Command
34 | exit func(int)
35 | }
36 |
37 | func main() {
38 | var root = &rootCmd{
39 | exit: os.Exit,
40 | }
41 |
42 | var cmd = &cobra.Command{
43 | Use: "gendoc",
44 | Short: "Generate documentation for Cider",
45 | Args: cobra.MaximumNArgs(1),
46 | SilenceUsage: true,
47 | SilenceErrors: true,
48 | DisableAutoGenTag: true,
49 | RunE: func(cmd *cobra.Command, args []string) error {
50 | for _, sub := range cmd.Commands() {
51 | if sub.Name() == "help" {
52 | continue
53 | }
54 | if err := sub.RunE(sub, args); err != nil {
55 | return err
56 | }
57 | }
58 |
59 | return nil
60 | },
61 | }
62 |
63 | cmd.AddCommand(
64 | cmdConfig(),
65 | cmdMan(),
66 | cmdMarkdown(),
67 | )
68 |
69 | root.cmd = cmd
70 |
71 | root.Execute(os.Args[1:])
72 | }
73 |
74 | func (cmd *rootCmd) Execute(args []string) {
75 | cmd.cmd.SetArgs(args)
76 |
77 | if err := cmd.cmd.Execute(); err != nil {
78 | var code = 1
79 |
80 | var msg = "command failed"
81 |
82 | log.WithError(err).Error(msg)
83 |
84 | cmd.exit(code)
85 | }
86 | }
87 |
88 | // CmdConfig returns the cobra.Command for the man subcommand.
89 | func cmdConfig() *cobra.Command {
90 | return &cobra.Command{
91 | Use: "config",
92 | Short: "Generate configuration file documentation for Cider.",
93 | Args: cobra.MaximumNArgs(1),
94 | RunE: runDocsConfigCmd,
95 | }
96 | }
97 |
98 | // CmdMan returns the cobra.Command for the man subcommand.
99 | func cmdMan() *cobra.Command {
100 | return &cobra.Command{
101 | Use: "man",
102 | Short: "Generate man documentation for Cider.",
103 | Args: cobra.MaximumNArgs(1),
104 | RunE: runDocsManCmd,
105 | }
106 | }
107 |
108 | // CmdMarkdown returns the cobra.Command for the man subcommand.
109 | func cmdMarkdown() *cobra.Command {
110 | return &cobra.Command{
111 | Use: "md",
112 | Short: "Generate Markdown documentation for Cider.",
113 | Args: cobra.MaximumNArgs(1),
114 | RunE: runDocsMdCmd,
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: build
2 | on:
3 | push:
4 | branches:
5 | - main
6 | tags:
7 | - v*
8 | paths-ignore:
9 | - '**.md'
10 | pull_request:
11 | paths-ignore:
12 | - '**.md'
13 |
14 | env:
15 | GO111MODULE: on
16 |
17 | jobs:
18 | tests:
19 | name: unit tests
20 | strategy:
21 | matrix:
22 | platform:
23 | - ubuntu-latest
24 | - windows-latest
25 | runs-on: ${{ matrix.platform }}
26 | steps:
27 | - uses: actions/checkout@v2
28 |
29 | - uses: actions/setup-go@v1
30 | with:
31 | go-version: '1.16'
32 |
33 | - name: Cache Go Modules
34 | uses: actions/cache@v2
35 | with:
36 | path: ~/go/pkg/mod
37 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
38 | restore-keys: |
39 | ${{ runner.os }}-go-
40 |
41 | - name: Run Tests
42 | run: go test -race -coverprofile coverage.out -covermode atomic ./...
43 |
44 | - name: Upload Coverage to Codecov
45 | if: success()
46 | uses: codecov/codecov-action@v1
47 | with:
48 | file: ./coverage.out
49 |
50 | lint:
51 | name: lint
52 | runs-on: ubuntu-latest
53 | steps:
54 | - uses: actions/checkout@v2
55 |
56 | - uses: actions/setup-go@v1
57 | with:
58 | go-version: '1.16'
59 |
60 | - name: golangci-lint
61 | uses: golangci/golangci-lint-action@master
62 | with:
63 | version: v1.39
64 | skip-go-installation: true
65 |
66 | verify_doc_tools:
67 | name: verify doc tools
68 | runs-on: ubuntu-latest
69 | steps:
70 | - uses: actions/checkout@v2
71 |
72 | - uses: actions/setup-go@v1
73 | with:
74 | go-version: '1.16'
75 |
76 | - name: Run gendoc tool
77 | run: go run ./tools/gendoc
78 |
79 | - name: Run licensing tool
80 | run: go run ./tools/licensing
81 |
82 | - name: Check working copy state
83 | run: git diff-index --quiet HEAD || git status --short
84 |
85 | release:
86 | name: release
87 | if: startsWith(github.ref, 'refs/tags/')
88 | needs:
89 | - tests
90 | - lint
91 | runs-on: ubuntu-latest
92 | steps:
93 | - uses: actions/checkout@v2
94 | with:
95 | fetch-depth: 0
96 |
97 | - uses: actions/setup-go@v1
98 | with:
99 | go-version: '1.16'
100 |
101 | - name: Login to Docker Hub
102 | run: |
103 | echo "${DOCKER_HUB_TOKEN}" | \
104 | docker login --username "${DOCKER_HUB_LOGIN}" --password-stdin
105 | env:
106 | DOCKER_HUB_LOGIN: ${{ secrets.DOCKER_HUB_LOGIN }}
107 | DOCKER_HUB_TOKEN: ${{ secrets.DOCKER_HUB_TOKEN }}
108 |
109 | - name: GoReleaser
110 | uses: goreleaser/goreleaser-action@v2
111 | with:
112 | version: latest
113 | args: release --rm-dist
114 | env:
115 | GITHUB_TOKEN: ${{ secrets.BREW_GITHUB_TOKEN }}
116 |
117 | - name: Clear
118 | if: always()
119 | run: rm -f ${HOME}/.docker/config.json
120 |
--------------------------------------------------------------------------------
/internal/template/template.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | // Package template provides an interface for text templates to be used during pipes
22 | package template
23 |
24 | import (
25 | "bytes"
26 | "path/filepath"
27 | "strings"
28 | "text/template"
29 | "time"
30 |
31 | "github.com/cidertool/cider/pkg/context"
32 | )
33 |
34 | const (
35 | versionKey = "version"
36 | envKey = "env"
37 | dateKey = "date"
38 | timestampKey = "timestamp"
39 | )
40 |
41 | // Template is used to apply text templates to strings to dynamically configure API values. See the documentation of
42 | // text/template to see the valid template format.
43 | type Template struct {
44 | fields Fields
45 | }
46 |
47 | // Fields is a heterogenous map type keyed by strings.
48 | type Fields map[string]interface{}
49 |
50 | // New returns a new template instance.
51 | func New(ctx *context.Context) *Template {
52 | return &Template{
53 | Fields{
54 | versionKey: ctx.Version,
55 | envKey: ctx.Env,
56 | dateKey: ctx.Date.UTC().Format(time.RFC3339),
57 | timestampKey: ctx.Date.UTC().Unix(),
58 | },
59 | }
60 | }
61 |
62 | // WithFields merges the template's configured fields with the given Fields.
63 | func (t *Template) WithFields(fields Fields) *Template {
64 | for key, value := range fields {
65 | t.fields[key] = value
66 | }
67 |
68 | return t
69 | }
70 |
71 | // WithEnv replaces the configured env of the template with the given key-value map.
72 | func (t *Template) WithEnv(env map[string]string) *Template {
73 | t.fields[envKey] = env
74 |
75 | return t
76 | }
77 |
78 | // WithShellEnv replaces the configured env of the template with the given sequence of shell-style, e.g. "KEY=VALUE",
79 | // strings.
80 | func (t *Template) WithShellEnv(envs ...string) *Template {
81 | env := make(map[string]string)
82 |
83 | for _, e := range envs {
84 | parts := strings.SplitN(e, "=", 2)
85 | env[parts[0]] = parts[1]
86 | }
87 |
88 | return t.WithEnv(env)
89 | }
90 |
91 | // Apply takes the template string and processes it into its product string.
92 | func (t *Template) Apply(s string) (string, error) {
93 | var out bytes.Buffer
94 |
95 | tmpl, err := template.New("tmpl").
96 | Option("missingkey=error").
97 | Funcs(template.FuncMap{
98 | "replace": strings.ReplaceAll,
99 | "lowercased": strings.ToLower,
100 | "uppercased": strings.ToUpper,
101 | "titlecased": strings.ToTitle,
102 | "dir": filepath.Dir,
103 | "abs": filepath.Abs,
104 | "rel": filepath.Rel,
105 | }).
106 | Parse(s)
107 | if err != nil {
108 | return "", err
109 | }
110 |
111 | err = tmpl.Execute(&out, t.fields)
112 |
113 | return out.String(), err
114 | }
115 |
--------------------------------------------------------------------------------
/internal/pipe/publish/publish_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package publish
22 |
23 | import (
24 | "testing"
25 |
26 | "github.com/cidertool/cider/internal/client/clienttest"
27 | "github.com/cidertool/cider/internal/pipe"
28 | "github.com/cidertool/cider/pkg/config"
29 | "github.com/cidertool/cider/pkg/context"
30 | "github.com/stretchr/testify/assert"
31 | )
32 |
33 | func TestPublish_String(t *testing.T) {
34 | t.Parallel()
35 |
36 | p := Pipe{}
37 | assert.Equal(t, "publishing from app store connect", p.String())
38 | }
39 |
40 | func TestPublish_Happy_Testflight(t *testing.T) {
41 | t.Parallel()
42 |
43 | ctx := context.New(config.Project{
44 | "TEST": {},
45 | })
46 | ctx.AppsToRelease = []string{"TEST"}
47 | ctx.Credentials = &clienttest.Credentials{}
48 | ctx.PublishMode = context.PublishModeTestflight
49 |
50 | p := Pipe{}
51 | p.client = &clienttest.Client{}
52 |
53 | err := p.Run(ctx)
54 | assert.NoError(t, err)
55 | }
56 |
57 | func TestPublish_Happy_Store(t *testing.T) {
58 | t.Parallel()
59 |
60 | ctx := context.New(config.Project{
61 | "TEST": {},
62 | })
63 | ctx.AppsToRelease = []string{"TEST"}
64 | ctx.Credentials = &clienttest.Credentials{}
65 | ctx.PublishMode = context.PublishModeAppStore
66 |
67 | p := Pipe{}
68 | p.client = &clienttest.Client{}
69 |
70 | err := p.Run(ctx)
71 | assert.NoError(t, err)
72 | }
73 |
74 | func TestPublish_Happy_NoApps(t *testing.T) {
75 | t.Parallel()
76 |
77 | ctx := context.New(config.Project{})
78 |
79 | p := Pipe{}
80 |
81 | err := p.Run(ctx)
82 | assert.EqualError(t, err, pipe.ErrSkipNoAppsToPublish.Error())
83 | }
84 |
85 | func TestPublish_Err_NoPublishMode(t *testing.T) {
86 | t.Parallel()
87 |
88 | ctx := context.New(config.Project{})
89 | ctx.AppsToRelease = []string{"TEST"}
90 |
91 | p := Pipe{}
92 |
93 | err := p.Run(ctx)
94 | assert.EqualError(t, err, errUnsupportedPublishMode{context.PublishMode("")}.Error())
95 | }
96 |
97 | func TestPublish_Err_AppMismatchTestflight(t *testing.T) {
98 | t.Parallel()
99 |
100 | ctx := context.New(config.Project{
101 | "TEST_": {},
102 | })
103 | ctx.AppsToRelease = []string{"_TEST"}
104 | ctx.Credentials = &clienttest.Credentials{}
105 | ctx.PublishMode = context.PublishModeTestflight
106 |
107 | p := Pipe{}
108 | p.client = &clienttest.Client{}
109 |
110 | err := p.Run(ctx)
111 | assert.Error(t, err)
112 | }
113 |
114 | func TestPublish_Err_AppMismatchStore(t *testing.T) {
115 | t.Parallel()
116 |
117 | ctx := context.New(config.Project{
118 | "TEST_": {},
119 | })
120 | ctx.AppsToRelease = []string{"_TEST"}
121 | ctx.Credentials = &clienttest.Credentials{}
122 | ctx.PublishMode = context.PublishModeAppStore
123 |
124 | p := Pipe{}
125 | p.client = &clienttest.Client{}
126 |
127 | err := p.Run(ctx)
128 | assert.Error(t, err)
129 | }
130 |
--------------------------------------------------------------------------------
/pkg/config/config_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package config
22 |
23 | import (
24 | "errors"
25 | "strings"
26 | "testing"
27 |
28 | "github.com/stretchr/testify/assert"
29 | )
30 |
31 | var errTestError = errors.New("test error")
32 |
33 | func TestValidConfiguration(t *testing.T) {
34 | t.Parallel()
35 |
36 | f, err := Load("testdata/valid.yml")
37 | assert.NoError(t, err)
38 | assert.Len(t, f, 1)
39 | }
40 |
41 | func TestMissingConfiguration(t *testing.T) {
42 | t.Parallel()
43 |
44 | _, err := Load("testdata/doesnotexist.yml")
45 | assert.Error(t, err)
46 | }
47 |
48 | func TestInvalidConfiguration(t *testing.T) {
49 | t.Parallel()
50 |
51 | _, err := Load("testdata/invalid.yml")
52 | assert.Error(t, err)
53 | }
54 |
55 | func TestMarshalledIsValidConfiguration(t *testing.T) {
56 | t.Parallel()
57 |
58 | f, err := Load("testdata/valid.yml")
59 | assert.NoError(t, err)
60 | str, err := f.String()
61 | assert.NoError(t, err)
62 | f2, err := LoadReader(strings.NewReader(str))
63 | assert.NoError(t, err)
64 | assert.Equal(t, f, f2)
65 | }
66 |
67 | func TestBrokenFile(t *testing.T) {
68 | t.Parallel()
69 |
70 | _, err := LoadReader(errReader(0))
71 | assert.Error(t, err)
72 | }
73 |
74 | type errReader int
75 |
76 | func (errReader) Read(p []byte) (int, error) {
77 | return 0, errTestError
78 | }
79 |
80 | func TestCopy(t *testing.T) {
81 | t.Parallel()
82 |
83 | p := Project{
84 | "App1": {},
85 | "App2": {},
86 | "App3": {},
87 | }
88 | pPrime, err := p.Copy()
89 | assert.NoError(t, err)
90 | assert.Equal(t, p, pPrime)
91 | assert.NotSame(t, p, pPrime)
92 | }
93 |
94 | func TestCopy_Err(t *testing.T) {
95 | t.Parallel()
96 |
97 | p := Project{
98 | "App1": {},
99 | "App2": {},
100 | "App3": {},
101 | }
102 | pPrime, err := p.Copy()
103 | assert.NoError(t, err)
104 | assert.Equal(t, p, pPrime)
105 | assert.NotSame(t, p, pPrime)
106 | }
107 |
108 | func TestAppsMatching(t *testing.T) {
109 | t.Parallel()
110 |
111 | p := Project{
112 | "App1": {},
113 | "App2": {},
114 | "App3": {},
115 | }
116 |
117 | var matches []string
118 | matches = p.AppsMatching([]string{"App1", "App2", "App3"}, false)
119 | assert.ElementsMatch(t, matches, []string{"App1", "App2", "App3"})
120 | matches = p.AppsMatching([]string{"App1", "App2"}, false)
121 | assert.ElementsMatch(t, matches, []string{"App1", "App2"})
122 | matches = p.AppsMatching([]string{"App1", "App2", "App4"}, false)
123 | assert.ElementsMatch(t, matches, []string{"App1", "App2"})
124 | matches = p.AppsMatching([]string{"", ""}, false)
125 | assert.ElementsMatch(t, matches, []string{})
126 | matches = p.AppsMatching([]string{"App1", "App2", "App4"}, true)
127 | assert.ElementsMatch(t, matches, []string{"App1", "App2", "App3"})
128 | matches = p.AppsMatching([]string{}, true)
129 | assert.ElementsMatch(t, matches, []string{"App1", "App2", "App3"})
130 | }
131 |
--------------------------------------------------------------------------------
/.goreleaser.yml:
--------------------------------------------------------------------------------
1 | before:
2 | hooks:
3 | - go mod download
4 |
5 | builds:
6 | - main: ./cmd/cider
7 | env:
8 | - CGO_ENABLED=0
9 | goos:
10 | - darwin
11 | - freebsd
12 | - linux
13 | - openbsd
14 | - windows
15 | goarch:
16 | - amd64
17 | - arm64
18 | mod_timestamp: '{{ .CommitTimestamp }}'
19 | flags:
20 | - -trimpath
21 | ldflags:
22 | - -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{ .CommitDate }} -X main.builtBy=cidertool
23 |
24 | checksum:
25 | name_template: 'checksums.txt'
26 |
27 | snapshot:
28 | name_template: '{{ .Tag }}-next'
29 |
30 | changelog:
31 | sort: asc
32 | filters:
33 | exclude:
34 | - '^docs:'
35 | - '^test:'
36 | - Merge pull request
37 | - Merge branch
38 | - go mod tidy
39 |
40 | dockers:
41 | - dockerfile: build/Dockerfile
42 | image_templates:
43 | - cidertool/cider:{{ .Tag }}
44 | - cidertool/cider:v{{ .Major }}
45 | - cidertool/cider:v{{ .Major }}.{{ .Minor }}
46 | - cidertool/cider:latest
47 | ids:
48 | - cider
49 | extra_files:
50 | - build/entrypoint.sh
51 | build_flag_templates:
52 | - --pull
53 | - --label=org.opencontainers.image.created={{ .Date }}
54 | - --label=org.opencontainers.image.name={{ .ProjectName }}
55 | - --label=org.opencontainers.image.revision={{ .FullCommit }}
56 | - --label=org.opencontainers.image.version={{ .Version }}
57 | - --label=org.opencontainers.image.source={{ .GitURL }}
58 | - --label=repository=http://github.com/cidertool/cider
59 | - --label=homepage=http://cidertool.github.io/cider
60 | - --label=maintainer=Aaron Sky
61 |
62 | archives:
63 | - name_template: '{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
64 | replacements:
65 | amd64: x86_64
66 | format_overrides:
67 | - goos: windows
68 | format: zip
69 |
70 | brews:
71 | - name: cider
72 | description: Submit your builds to the Apple App Store in seconds
73 | homepage: https://cidertool.github.io/cider
74 | tap:
75 | owner: cidertool
76 | name: homebrew-tap
77 | folder: Formula
78 | commit_author:
79 | name: cider-bot
80 | email: cider@skyaaron.com
81 | test: |
82 | system "#{bin}/cider", "-v"
83 | # - name: cider-core
84 | # description: Submit your builds to the Apple App Store in seconds
85 | # homepage: https://cidertool.github.io/cider
86 | # tap:
87 | # owner: cidertool
88 | # name: homebrew-tap
89 | # folder: Formula
90 | # commit_author:
91 | # name: cider-bot
92 | # email: cider@skyaaron.com
93 | # dependencies:
94 | # - name: go
95 | # type: build
96 | # install: |
97 | # system "go", "build", "-ldflags",
98 | # "-s -w -X main.version=#{version} -X main.commit=#{stable.specs[:revision]} -X main.builtBy=homebrew",
99 | # *std_go_args
100 | # man1.install "docs/man/cider.1"
101 | # man1.install "docs/man/cider_init.1"
102 | # man1.install "docs/man/cider_release.1"
103 | # man1.install "docs/man/cider_check.1"
104 | # man1.install "docs/man/cider_completions.1"
105 | # test: |
106 | # system "#{bin}/cider", "-v"
107 |
108 | scoop:
109 | description: Submit your builds to the Apple App Store in seconds
110 | homepage: https://cidertool.github.io/cider
111 | license: GPL-3.0-or-later
112 | bucket:
113 | owner: cidertool
114 | name: scoop-bucket
115 | commit_author:
116 | name: cider-bot
117 | email: cider@skyaaron.com
118 |
--------------------------------------------------------------------------------
/internal/client/clienttest/clienttest_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package clienttest_test
22 |
23 | import (
24 | "testing"
25 |
26 | "github.com/cidertool/cider/internal/client/clienttest"
27 | "github.com/cidertool/cider/pkg/config"
28 | "github.com/cidertool/cider/pkg/context"
29 | "github.com/stretchr/testify/assert"
30 | )
31 |
32 | func TestClient(t *testing.T) {
33 | t.Parallel()
34 |
35 | ctx := context.New(config.Project{})
36 |
37 | c := clienttest.Client{}
38 |
39 | app, err := c.GetAppForBundleID(ctx, "TEST")
40 | assert.NoError(t, err)
41 | assert.NotNil(t, app)
42 |
43 | info, err := c.GetAppInfo(ctx, "TEST")
44 | assert.NoError(t, err)
45 | assert.NotNil(t, info)
46 |
47 | build, err := c.GetBuild(ctx, nil)
48 | assert.NoError(t, err)
49 | assert.NotNil(t, build)
50 |
51 | initial, err := c.ReleaseForAppIsInitial(ctx, "TEST")
52 | assert.NoError(t, err)
53 | assert.False(t, initial)
54 |
55 | err = c.UpdateBetaAppLocalizations(ctx, "TEST", config.TestflightLocalizations{})
56 | assert.NoError(t, err)
57 |
58 | err = c.UpdateBetaBuildDetails(ctx, "TEST", config.Testflight{})
59 | assert.NoError(t, err)
60 |
61 | err = c.UpdateBetaBuildLocalizations(ctx, "TEST", config.TestflightLocalizations{})
62 | assert.NoError(t, err)
63 |
64 | err = c.UpdateBetaLicenseAgreement(ctx, "TEST", config.Testflight{})
65 | assert.NoError(t, err)
66 |
67 | err = c.AssignBetaGroups(ctx, "TEST", "TEST", []config.BetaGroup{})
68 | assert.NoError(t, err)
69 |
70 | err = c.AssignBetaTesters(ctx, "TEST", "TEST", []config.BetaTester{})
71 | assert.NoError(t, err)
72 |
73 | err = c.UpdateBetaReviewDetails(ctx, "TEST", config.ReviewDetails{})
74 | assert.NoError(t, err)
75 |
76 | err = c.SubmitBetaApp(ctx, "TEST")
77 | assert.NoError(t, err)
78 |
79 | err = c.UpdateApp(ctx, "TEST", "TEST", "TEST", config.App{})
80 | assert.NoError(t, err)
81 |
82 | err = c.UpdateAppLocalizations(ctx, "TEST", config.AppLocalizations{})
83 | assert.NoError(t, err)
84 |
85 | version, err := c.CreateVersionIfNeeded(ctx, "TEST", "TEST", config.Version{})
86 | assert.NoError(t, err)
87 | assert.NotNil(t, version)
88 |
89 | err = c.UpdateVersionLocalizations(ctx, "TEST", config.VersionLocalizations{})
90 | assert.NoError(t, err)
91 |
92 | err = c.UpdateIDFADeclaration(ctx, "TEST", config.IDFADeclaration{})
93 | assert.NoError(t, err)
94 |
95 | err = c.UploadRoutingCoverage(ctx, "TEST", config.File{})
96 | assert.NoError(t, err)
97 |
98 | err = c.UpdateReviewDetails(ctx, "TEST", config.ReviewDetails{})
99 | assert.NoError(t, err)
100 |
101 | err = c.EnablePhasedRelease(ctx, "TEST")
102 | assert.NoError(t, err)
103 |
104 | err = c.SubmitApp(ctx, "TEST")
105 | assert.NoError(t, err)
106 |
107 | proj, err := c.Project()
108 | assert.NoError(t, err)
109 | assert.NotNil(t, proj)
110 | }
111 |
112 | func TestCredentials(t *testing.T) {
113 | t.Parallel()
114 |
115 | c := clienttest.Credentials{}
116 | assert.NotNil(t, c.Client())
117 | }
118 |
--------------------------------------------------------------------------------
/internal/pipe/env/env_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package env
22 |
23 | import (
24 | "fmt"
25 | "os"
26 | "testing"
27 |
28 | "github.com/cidertool/cider/pkg/config"
29 | "github.com/cidertool/cider/pkg/context"
30 | "github.com/stretchr/testify/assert"
31 | )
32 |
33 | func TestEnv(t *testing.T) {
34 | t.Parallel()
35 |
36 | ctx := context.New(config.Project{})
37 |
38 | pipe := Pipe{}
39 |
40 | var err error
41 |
42 | assert.Equal(t, "loading environment variables", pipe.String())
43 |
44 | err = pipe.Run(ctx)
45 | assert.Error(t, err)
46 |
47 | // Pass with ASC_KEY_ID but fail on ASC_ISSUER_ID
48 | err = os.Setenv("ASC_KEY_ID", "TEST")
49 | assert.NoError(t, err)
50 | err = pipe.Run(ctx)
51 | assert.Error(t, err)
52 |
53 | // Pass with ASC_ISSUER_ID but fail on ASC_PRIVATE_KEY_PATH
54 | err = os.Setenv("ASC_ISSUER_ID", "TEST")
55 | assert.NoError(t, err)
56 | err = pipe.Run(ctx)
57 | assert.Error(t, err)
58 |
59 | // Check ASC_PRIVATE_KEY_PATH but fail because nothing exists at that path
60 | err = os.Setenv("ASC_PRIVATE_KEY_PATH", "TEST")
61 | assert.NoError(t, err)
62 | err = pipe.Run(ctx)
63 | assert.Error(t, err)
64 |
65 | // Check ASC_PRIVATE_KEY_PATH but silently fail because it isn't required
66 | err = os.Setenv("TEST", "path/to/no/file")
67 | assert.NoError(t, err)
68 | env, err := loadEnvFromPath("TEST", false)
69 | assert.NoError(t, err)
70 | assert.Empty(t, env)
71 |
72 | file, err := os.CreateTemp("", "fake_key")
73 | if err != nil {
74 | assert.FailNow(t, "temp file creation produced an error", err)
75 | }
76 |
77 | defer rmFile(file)
78 |
79 | // Check ASC_PRIVATE_KEY_PATH but fail because the contents of the file do not contain a real key
80 | err = os.Setenv("ASC_PRIVATE_KEY_PATH", file.Name())
81 | assert.NoError(t, err)
82 | err = pipe.Run(ctx)
83 | assert.Error(t, err)
84 |
85 | // This key is a mock key generated by the following command:
86 | //
87 | // openssl ecparam -name prime256v1 -genkey -noout | openssl pkcs8 -topk8 -nocrypt -out key.pem
88 | //
89 | // This will generate the ASN.1 PKCS#8 representation of the private key needed
90 | // to create a valid token. If you are looking at this test to see how to make a key,
91 | // reference Apple's documentation on this subject instead.
92 | //
93 | // https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api
94 | key := `
95 | -----BEGIN PRIVATE KEY-----
96 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgELiCwZa9oGedoUR7
97 | 8Vr36M6WOkEBGZh2YsUVL0kCIJ6hRANCAASQtP/ZdZBW6UdwJeyz09ws2nx5OOUA
98 | tra43bY9mLeVK0zrTn/3jvjTHEdD3HcRJgau1jshXG4IHXSW9yXj9x3V
99 | -----END PRIVATE KEY-----
100 | `
101 | _, err = file.WriteString(key)
102 | assert.NoError(t, err)
103 |
104 | // Pass with ASC_PRIVATE_KEY_PATH, and create the credentials object
105 | err = pipe.Run(ctx)
106 | assert.NoError(t, err)
107 | assert.NotNil(t, ctx.Credentials)
108 | }
109 |
110 | // rmFile closes an open descriptor.
111 | func rmFile(f *os.File) {
112 | if err := os.Remove(f.Name()); err != nil {
113 | // nolint: forbidigo
114 | fmt.Println(err)
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/internal/shell/shelltest/shelltest.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | // Package shelltest provides utilities for mocking the login shell.
22 | package shelltest
23 |
24 | import (
25 | "fmt"
26 | "os/exec"
27 | "strconv"
28 | "testing"
29 |
30 | "github.com/cidertool/cider/internal/shell"
31 | "github.com/cidertool/cider/pkg/context"
32 | "github.com/stretchr/testify/assert"
33 | )
34 |
35 | // ErrCommandOverflow happens when the command that is trying to be run would result in a
36 | // buffer overflow in the Shell mock.
37 | type ErrCommandOverflow struct {
38 | Index int
39 | Len int
40 | Command string
41 | }
42 |
43 | func (e ErrCommandOverflow) Error() string {
44 | return fmt.Sprintf("command out of bounds: i=%d,len=%d (command: `%s`)", e.Index, e.Len, e.Command)
45 | }
46 |
47 | // Shell is a type that conforms to shell.Shell.
48 | type Shell struct {
49 | T *testing.T
50 | Context *context.Context
51 | SupportedPrograms map[string]bool
52 | Commands []Command
53 | index int
54 | expectOverflowError bool
55 | }
56 |
57 | // Command represents the result of some executed command.
58 | type Command struct {
59 | ReturnCode int
60 | Stdout string
61 | Stderr string
62 | }
63 |
64 | // NewCommand takes a program and arguments and constructs a new exec.Cmd instance.
65 | func (sh *Shell) NewCommand(name string, arg ...string) *exec.Cmd {
66 | return exec.Command(name, arg...) // #nosec
67 | }
68 |
69 | // Exec executes the command.
70 | func (sh *Shell) Exec(cmd *exec.Cmd) (*shell.CompletedProcess, error) {
71 | if sh.index >= len(sh.Commands) {
72 | err := ErrCommandOverflow{
73 | Index: sh.index,
74 | Len: len(sh.Commands),
75 | Command: cmd.String(),
76 | }
77 |
78 | if !sh.expectOverflowError {
79 | assert.FailNow(sh.T, err.Error())
80 | }
81 |
82 | return nil, err
83 | }
84 |
85 | currentCommand := sh.Commands[sh.index]
86 | ps := shell.CompletedProcess{
87 | Name: cmd.Path,
88 | Args: cmd.Args,
89 | ReturnCode: currentCommand.ReturnCode,
90 | Stdout: currentCommand.Stdout,
91 | Stderr: currentCommand.Stderr,
92 | }
93 | sh.index++
94 |
95 | var err error
96 | if currentCommand.ReturnCode != 0 {
97 | err = &shellError{
98 | Process: ps,
99 | }
100 | }
101 |
102 | return &ps, err
103 | }
104 |
105 | // Exists returns whether the given program exists.
106 | //
107 | // This implementation of the method returns true if the SupportedPrograms
108 | // field is empty, otherwise it checks the value of the key in that map.
109 | // Use SupportedPrograms to mock the existence of a program in the PATH.
110 | func (sh *Shell) Exists(program string) bool {
111 | if len(sh.SupportedPrograms) == 0 {
112 | return true
113 | }
114 |
115 | return sh.SupportedPrograms[program]
116 | }
117 |
118 | // CurrentDirectory returns the current directory.
119 | func (sh *Shell) CurrentDirectory() string {
120 | return sh.Context.CurrentDirectory
121 | }
122 |
123 | type shellError struct {
124 | Process shell.CompletedProcess
125 | }
126 |
127 | func (err *shellError) Error() string {
128 | return strconv.Itoa(err.Process.ReturnCode)
129 | }
130 |
--------------------------------------------------------------------------------
/internal/shell/shell.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | // Package shell wraps shell execution
22 | package shell
23 |
24 | import (
25 | "bytes"
26 | "os/exec"
27 | "strings"
28 |
29 | "github.com/alessio/shellescape"
30 | "github.com/cidertool/cider/internal/log"
31 | "github.com/cidertool/cider/pkg/context"
32 | )
33 |
34 | // Shell is an abstraction for shell-program execution meant to make
35 | // testing and client design easier.
36 | type Shell interface {
37 | NewCommand(program string, args ...string) *exec.Cmd
38 | Exec(cmd *exec.Cmd) (*CompletedProcess, error)
39 | Exists(program string) bool
40 | CurrentDirectory() string
41 | }
42 |
43 | // New returns a new shell bound to the provided context.
44 | func New(ctx *context.Context) Shell {
45 | return &loginShell{ctx}
46 | }
47 |
48 | // CompletedProcess represents a subshell execution that finished, and includes its arguments, return code, and
49 | // standard buffers as strings.
50 | type CompletedProcess struct {
51 | Name string
52 | Args []string
53 | ReturnCode int
54 | Stdout string
55 | Stderr string
56 | }
57 |
58 | func newCompletedProcess(cmd *exec.Cmd) *CompletedProcess {
59 | stdout, ok := cmd.Stdout.(*bytes.Buffer)
60 | if !ok {
61 | return nil
62 | }
63 |
64 | stderr, ok := cmd.Stderr.(*bytes.Buffer)
65 | if !ok {
66 | return nil
67 | }
68 |
69 | var stdoutString, stderrString string
70 |
71 | if stdout != nil {
72 | stdoutString = strings.TrimSpace(stdout.String())
73 | }
74 |
75 | if stderr != nil {
76 | stderrString = strings.TrimSpace(stderr.String())
77 | }
78 |
79 | return &CompletedProcess{
80 | Name: cmd.Path,
81 | Args: cmd.Args,
82 | ReturnCode: cmd.ProcessState.ExitCode(),
83 | Stdout: stdoutString,
84 | Stderr: stderrString,
85 | }
86 | }
87 |
88 | // loginShell is an empty struct that implements shell.Shell with default
89 | // os.Exec subshell execution logic.
90 | type loginShell struct {
91 | *context.Context
92 | }
93 |
94 | // NewCommand takes a program name and series of arguments and constructs an
95 | // exec.Cmd object that can be manipulated and fed to Exec().
96 | func (sh *loginShell) NewCommand(program string, arg ...string) *exec.Cmd {
97 | cmd := exec.CommandContext(sh.Context, program, escapeArgs(arg)...) // #nosec
98 |
99 | var stdout, stderr bytes.Buffer
100 | cmd.Stdout = &stdout
101 | cmd.Stderr = &stderr
102 | cmd.Dir = sh.Context.CurrentDirectory
103 |
104 | return cmd
105 | }
106 |
107 | func escapeArgs(args []string) []string {
108 | cp := make([]string, len(args))
109 |
110 | for i, arg := range args {
111 | cp[i] = shellescape.Quote(arg)
112 | }
113 |
114 | return cp
115 | }
116 |
117 | // Exec executes a command.
118 | func (sh *loginShell) Exec(cmd *exec.Cmd) (proc *CompletedProcess, err error) {
119 | sh.Context.Log.WithField("args", cmd.Args).Debug(cmd.Path)
120 |
121 | err = cmd.Run()
122 | proc = newCompletedProcess(cmd)
123 |
124 | if proc == nil {
125 | sh.Context.Log.Debugf("last process failed to complete coherently")
126 | } else {
127 | sh.Context.Log.WithFields(log.Fields{
128 | "code": proc.ReturnCode,
129 | "stdout": strings.TrimSpace(proc.Stdout),
130 | "stderr": strings.TrimSpace(proc.Stderr),
131 | }).Debugf("%s result", proc.Name)
132 | }
133 |
134 | return proc, err
135 | }
136 |
137 | // Exists returns whether or not a given program is installed.
138 | func (sh *loginShell) Exists(program string) bool {
139 | path, err := exec.LookPath(program)
140 | if err != nil {
141 | return false
142 | }
143 |
144 | return path != ""
145 | }
146 |
147 | func (sh *loginShell) CurrentDirectory() string {
148 | return sh.Context.CurrentDirectory
149 | }
150 |
--------------------------------------------------------------------------------
/internal/client/util_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package client
22 |
23 | import (
24 | "encoding/json"
25 | "fmt"
26 | "net/http"
27 | "net/http/httptest"
28 | "net/url"
29 | "os"
30 | "path"
31 | "path/filepath"
32 | "testing"
33 |
34 | "github.com/cidertool/cider/internal/log"
35 | "github.com/cidertool/cider/pkg/config"
36 | "github.com/cidertool/cider/pkg/context"
37 | "github.com/stretchr/testify/assert"
38 | )
39 |
40 | type response struct {
41 | StatusCode int
42 | RawResponse string
43 | Response interface{}
44 | }
45 |
46 | type testContext struct {
47 | Context *context.Context
48 | Responses []response
49 | CurrentResponseIndex int
50 | server *httptest.Server
51 | }
52 |
53 | type mockCredentials struct {
54 | url string
55 | client *http.Client
56 | }
57 |
58 | type mockTransport struct {
59 | URL *url.URL
60 | Transport http.RoundTripper
61 | }
62 |
63 | type testAsset struct {
64 | Name string
65 | Size int64
66 | }
67 |
68 | func newTestContext(resp ...response) (*testContext, Client) {
69 | ctx := testContext{}
70 |
71 | ctx.Context = context.New(config.Project{})
72 |
73 | server := httptest.NewServer(&ctx)
74 | ctx.Context.Credentials = &mockCredentials{
75 | url: server.URL,
76 | client: server.Client(),
77 | }
78 | ctx.Responses = resp
79 | ctx.server = server
80 | client := New(ctx.Context)
81 |
82 | return &ctx, client
83 | }
84 |
85 | func (c *testContext) Close() {
86 | c.server.Close()
87 | }
88 |
89 | func (c *testContext) ServeHTTP(w http.ResponseWriter, r *http.Request) {
90 | if c.CurrentResponseIndex >= len(c.Responses) {
91 | c.Context.Log.WithFields(log.Fields{
92 | "currentResponseIndex": c.CurrentResponseIndex,
93 | "responsesCount": len(c.Responses),
94 | "url": r.URL,
95 | }).Fatal("index out of bounds")
96 | }
97 |
98 | c.Context.Log.WithFields(log.Fields{
99 | "progress": fmt.Sprintf("(%d/%d)", c.CurrentResponseIndex, len(c.Responses)),
100 | "req_method": r.Method,
101 | "req_url": r.URL,
102 | }).Info("responding")
103 |
104 | resp := c.Responses[c.CurrentResponseIndex]
105 |
106 | if resp.StatusCode == 0 {
107 | resp.StatusCode = http.StatusOK
108 | }
109 |
110 | w.WriteHeader(resp.StatusCode)
111 |
112 | var body = resp.RawResponse
113 |
114 | if resp.Response != nil {
115 | b, err := json.Marshal(resp.Response)
116 | if err == nil {
117 | body = string(b)
118 | }
119 | }
120 |
121 | if body == "" {
122 | body = `{}`
123 | }
124 |
125 | fmt.Fprintln(w, body)
126 | c.CurrentResponseIndex++
127 | }
128 |
129 | func (c *testContext) SetResponses(resp ...response) {
130 | c.Responses = resp
131 | c.CurrentResponseIndex = 0
132 | }
133 |
134 | func (c *testContext) URL(rawpath string) (*url.URL, error) {
135 | return url.Parse(c.server.URL + "/" + rawpath)
136 | }
137 |
138 | func (c *testAsset) URL(ctx *testContext, id string) (*url.URL, error) {
139 | return ctx.URL(id)
140 | }
141 |
142 | func (c *mockCredentials) Client() *http.Client {
143 | url, _ := url.Parse(c.url)
144 | c.client.Transport = &mockTransport{URL: url, Transport: c.client.Transport}
145 |
146 | return c.client
147 | }
148 |
149 | func (t *mockTransport) RoundTrip(req *http.Request) (*http.Response, error) {
150 | newURL := *t.URL
151 | newURL.Path = path.Join(newURL.Path, req.URL.Path)
152 | req.URL = &newURL
153 |
154 | var transport http.RoundTripper
155 | if t.Transport == nil {
156 | transport = http.DefaultTransport
157 | } else {
158 | transport = t.Transport
159 | }
160 |
161 | return transport.RoundTrip(req)
162 | }
163 |
164 | func newTestAsset(t *testing.T, name string) *testAsset {
165 | t.Helper()
166 |
167 | var path = filepath.Join(t.TempDir(), name)
168 |
169 | err := os.WriteFile(path, []byte("TEST"), 0600)
170 | assert.NoError(t, err)
171 |
172 | info, err := os.Stat(path)
173 | assert.NoError(t, err)
174 |
175 | return &testAsset{
176 | Name: path,
177 | Size: info.Size(),
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/docs/man/cider_release.1:
--------------------------------------------------------------------------------
1 | .nh
2 | .TH "CIDER\-RELEASE" "1" "Apr 2021" "Auto generated by spf13/cobra" ""
3 |
4 | .SH NAME
5 | .PP
6 | cider\-release \- Release the selected apps in the current project
7 |
8 |
9 | .SH SYNOPSIS
10 | .PP
11 | \fBcider release [path] [flags]\fP
12 |
13 |
14 | .SH DESCRIPTION
15 | .PP
16 | Release the selected apps in the current project.
17 |
18 | .PP
19 | You can provide a path to a project directory as an argument to be the root directory
20 | of all relative path expansions in the program, such as the Git repository, preview sets,
21 | and screenshot resources. The only exception to this is if you provide a custom configuration
22 | file path with the \fB\fC\-\-config\fR flag. Instead, that file will be loaded relative to
23 | the working directory of the Cider process itself.
24 |
25 | .PP
26 | Additionally, Cider requires a few environment variables to be set in order to operate.
27 | They each correspond to an element of authorization described by the Apple Developer Documentation.
28 |
29 | .RS
30 | .IP \(bu 2
31 | \fB\fCASC\_KEY\_ID\fR: The key's ID.
32 | .IP \(bu 2
33 | \fB\fCASC\_ISSUER\_ID\fR: Your team's issuer ID.
34 | .IP \(bu 2
35 | \fB\fCASC\_PRIVATE\_KEY\fR or \fB\fCASC\_PRIVATE\_KEY\_PATH\fR: The .p8 private key issued by Apple. Must belong to an App Manager, Admin, or Account Holder.
36 |
37 | .RE
38 |
39 | .PP
40 | These three values each have varying degrees of sensetivity and should be treated as secrets. Store
41 | them securely in your environment so Cider can leverage them safely.
42 |
43 | .PP
44 | More info: https://developer.apple.com/documentation/appstoreconnectapi/creating\_api\_keys\_for\_app\_store\_connect\_api
45 |
46 |
47 | .SH OPTIONS
48 | .PP
49 | \fB\-A\fP, \fB\-\-all\-apps\fP[=false]
50 | Process all apps in the configuration file. Supercedes any usage of the \fB\fC\-\-app\fR flag.
51 |
52 | .PP
53 | \fB\-a\fP, \fB\-\-app\fP=[]
54 | Process the given app, providing the app key name used in your configuration file.
55 |
56 | .PP
57 | This flag can be provided repeatedly for each app you want to process. You can omit
58 | this flag if your configuration file has only one app defined.
59 |
60 | .PP
61 | \fB\-f\fP, \fB\-\-config\fP=""
62 | Load configuration from file
63 |
64 | .PP
65 | \fB\-h\fP, \fB\-\-help\fP[=false]
66 | help for release
67 |
68 | .PP
69 | \fB\-p\fP, \fB\-\-max\-processes\fP=1
70 | Run certain metadata syncing and asset uploading logic in parallel with
71 | the maximum allowable concurrency.
72 |
73 | .PP
74 | \fB\-\-mode\fP=
75 | Mode used to declare the publishing target for submission.
76 |
77 | .PP
78 | The default is "testflight" for submitting to Testflight, and the other alternative
79 | option is "appstore" for submitting to the App Store.
80 |
81 | .PP
82 | \fB\-\-set\-beta\-group\fP=[]
83 | Provide names of beta groups to release to instead of using
84 | the configuration file.
85 |
86 | .PP
87 | \fB\-\-set\-beta\-tester\fP=[]
88 | Provide email addresses of beta testers to release to instead of
89 | using the configuration file.
90 |
91 | .PP
92 | \fB\-B\fP, \fB\-\-set\-build\fP=""
93 | Build override to use instead of "latest". Corresponds to the CFBundleVersion
94 | of your build.
95 |
96 | .PP
97 | The default behavior without this flag is to select the latest build. In both cases,
98 | if the selected build has an invalid processing state, Cider will abort with an error
99 | to ensure your release is handled safely.
100 |
101 | .PP
102 | \fB\-V\fP, \fB\-\-set\-version\fP=""
103 | Version string override to use instead of parsing Git tags. Corresponds to the
104 | CFBundleShortVersionString of your build.
105 |
106 | .PP
107 | Cider expects this string to follow the Major.Minor.Patch semantics outlined in Apple documentation
108 | and Semantic Versioning (semver). If this flag is omitted, Git will be leveraged to determine the
109 | latest tag. The tag will be used to calculate the version string under the same constraints.
110 |
111 | .PP
112 | \fB\-\-skip\-git\fP[=false]
113 | Skips deriving version information from Git. Must only be used in conjunction with the \fB\fC\-\-set\-version\fR flag.
114 |
115 | .PP
116 | \fB\-\-skip\-submit\fP[=false]
117 | Skips submitting for review
118 |
119 | .PP
120 | \fB\-\-skip\-update\-metadata\fP[=false]
121 | Skips updating metadata (app info, localizations, assets, review details, etc.)
122 |
123 | .PP
124 | \fB\-\-skip\-update\-pricing\fP[=false]
125 | Skips updating app pricing
126 |
127 | .PP
128 | \fB\-\-timeout\fP=30m0s
129 | Timeout for the entire release process.
130 |
131 | .PP
132 | If the command takes longer than this amount of time to run, Cider will abort.
133 |
134 |
135 | .SH OPTIONS INHERITED FROM PARENT COMMANDS
136 | .PP
137 | \fB\-\-debug\fP[=false]
138 | Enable debug mode
139 |
140 |
141 | .SH EXAMPLE
142 | .PP
143 | .RS
144 |
145 | .nf
146 | cider release \-\-mode=appstore \-\-set\-version="1.0"
147 |
148 | .fi
149 | .RE
150 |
151 |
152 | .SH SEE ALSO
153 | .PP
154 | \fBcider(1)\fP
155 |
--------------------------------------------------------------------------------
/tools/gendoc/config.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package main
22 |
23 | import (
24 | "fmt"
25 | "os"
26 | "path/filepath"
27 |
28 | "github.com/apex/log"
29 | "github.com/cidertool/asc-go/asc"
30 | "github.com/cidertool/cider/internal/closer"
31 | "github.com/cidertool/cider/pkg/config"
32 | "github.com/spf13/cobra"
33 | )
34 |
35 | const (
36 | docsConfigFrontmatterTemplate = `---
37 | layout: page
38 | nav_order: %d
39 | ---
40 |
41 | # Configuration
42 | {: .no_toc }
43 |
44 | `
45 | docsConfigTableOfContents = `
46 |
47 |
48 | Table of Contents
49 |
50 | {: .text-delta }
51 | - TOC
52 | {:toc}
53 |
54 |
55 | `
56 | docsConfigTerminologyDisclaimer = `
57 | - [x] An X here means the field is required.
58 | - [ ] This field is optional and can be omitted.
59 |
60 | `
61 | )
62 |
63 | // nolint: gochecknoglobals
64 | var docsConfigExampleProject = config.Project{
65 | "My App": {
66 | BundleID: "com.myproject.MyApp",
67 | PrimaryLocale: "en-US",
68 | UsesThirdPartyContent: asc.Bool(false),
69 | Availability: &config.Availability{
70 | AvailableInNewTerritories: asc.Bool(false),
71 | Pricing: []config.PriceSchedule{
72 | {Tier: "0"},
73 | },
74 | Territories: []string{"USA"},
75 | },
76 | Categories: &config.Categories{
77 | Primary: "SOCIAL_NETWORKING",
78 | Secondary: "GAMES",
79 | SecondarySubcategories: [2]string{
80 | "GAMES_SIMULATION",
81 | "GAMES_RACING",
82 | },
83 | },
84 | Localizations: config.AppLocalizations{
85 | "en-US": {
86 | Name: "My App",
87 | Subtitle: "Not Your App",
88 | },
89 | },
90 | Versions: config.Version{
91 | Platform: config.PlatformiOS,
92 | Copyright: "2020 Me",
93 | EarliestReleaseDate: nil,
94 | ReleaseType: config.ReleaseTypeAfterApproval,
95 | PhasedReleaseEnabled: true,
96 | IDFADeclaration: nil,
97 | Localizations: config.VersionLocalizations{
98 | "en-US": {
99 | Description: "My App for cool people",
100 | Keywords: "Apps, Cool, Mine",
101 | WhatsNewText: `Thank you for using My App! I bring you updates every week so this continues to be my app.`,
102 | PreviewSets: config.PreviewSets{
103 | config.PreviewTypeiPhone65: []config.Preview{
104 | {
105 | File: config.File{
106 | Path: "assets/store/iphone65/preview.mp4",
107 | },
108 | },
109 | },
110 | },
111 | ScreenshotSets: config.ScreenshotSets{
112 | config.ScreenshotTypeiPhone65: []config.File{
113 | {Path: "assets/store/iphone65/app.jpg"},
114 | },
115 | },
116 | },
117 | },
118 | },
119 | Testflight: config.Testflight{
120 | EnableAutoNotify: true,
121 | Localizations: config.TestflightLocalizations{
122 | "en-US": {
123 | Description: "My App for cool people using the beta",
124 | },
125 | },
126 | },
127 | },
128 | }
129 |
130 | func runDocsConfigCmd(cmd *cobra.Command, args []string) error {
131 | var path string
132 | if len(args) == 0 {
133 | path = defaultDocsPath
134 | } else {
135 | path = args[0]
136 | }
137 |
138 | path = filepath.Join(path, "configuration.md")
139 | log.WithField("path", path).Info("generating configuration documentation")
140 |
141 | err := genConfigMarkdown(path)
142 | if err != nil {
143 | log.Error("generation failed")
144 | } else {
145 | log.Info("generation completed successfully")
146 | }
147 |
148 | return err
149 | }
150 |
151 | func genConfigMarkdown(path string) error {
152 | f, err := os.Create(path)
153 | if err != nil {
154 | return err
155 | }
156 |
157 | defer closer.Close(f)
158 |
159 | r, err := newRenderer()
160 | if err != nil {
161 | return err
162 | }
163 |
164 | r.Header = func() string {
165 | return fmt.Sprintf(docsConfigFrontmatterTemplate, 4)
166 | }
167 |
168 | r.Footer = func() string {
169 | contents, err := os.ReadFile(filepath.Join(filepath.Dir(path), "configuration-footer.md"))
170 | if err != nil {
171 | log.Error(err.Error())
172 |
173 | return ""
174 | }
175 |
176 | return string(contents)
177 | }
178 |
179 | return r.Render(f)
180 | }
181 |
--------------------------------------------------------------------------------
/internal/git/git.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | // Package git provides an integration with the git command
22 | package git
23 |
24 | import (
25 | "fmt"
26 | "path/filepath"
27 | "strings"
28 |
29 | "github.com/cidertool/cider/internal/shell"
30 | "github.com/cidertool/cider/pkg/context"
31 | )
32 |
33 | // Repo represents any kind of repo (github, gitlab, etc).
34 | type Repo struct {
35 | Owner string
36 | Name string
37 | }
38 |
39 | // Git wraps a shell.Shell provider to provide an interface over
40 | // the git program in the PATH.
41 | type Git struct {
42 | shell.Shell
43 | }
44 |
45 | // New constructs a new Git instance with a default shell provider
46 | // based on the login shell.
47 | func New(ctx *context.Context) *Git {
48 | return &Git{
49 | Shell: shell.New(ctx),
50 | }
51 | }
52 |
53 | // Run runs a Git command and returns its output or errors.
54 | func (git *Git) Run(args ...string) (*shell.CompletedProcess, error) {
55 | return git.RunInEnv(nil, args...)
56 | }
57 |
58 | // RunInEnv runs a Git command with the specified env vars and returns its output or errors.
59 | func (git *Git) RunInEnv(env map[string]string, args ...string) (*shell.CompletedProcess, error) {
60 | var extraArgs = []string{
61 | "-c", "log.showSignature=false",
62 | }
63 |
64 | cd := git.CurrentDirectory()
65 | if cd != "" && cd != "." {
66 | extraArgs = append(extraArgs, "-C", filepath.Clean(cd))
67 | }
68 |
69 | args = append(extraArgs, args...)
70 |
71 | var cmd = git.Shell.NewCommand("git", args...)
72 |
73 | if env != nil {
74 | cmd.Env = []string{}
75 | for k, v := range env {
76 | cmd.Env = append(cmd.Env, k+"="+v)
77 | }
78 | }
79 |
80 | return git.Shell.Exec(cmd)
81 | }
82 |
83 | // IsRepo returns true if current folder is a Git repository.
84 | func (git *Git) IsRepo() bool {
85 | proc, err := git.Run("rev-parse", "--is-inside-work-tree")
86 |
87 | return err == nil && strings.TrimSpace(proc.Stdout) == "true"
88 | }
89 |
90 | // SanitizedError wraps the contents of a sanitized Git error.
91 | type SanitizedError struct {
92 | stderr string
93 | }
94 |
95 | func (e SanitizedError) Error() string {
96 | return e.stderr
97 | }
98 |
99 | // SanitizeProcess cleans up the output.
100 | func (git *Git) SanitizeProcess(proc *shell.CompletedProcess, err error) (string, error) {
101 | var out string
102 |
103 | if proc != nil {
104 | firstline := strings.Split(proc.Stdout, "\n")[0]
105 | out = strings.ReplaceAll(firstline, "'", "")
106 |
107 | if err != nil {
108 | err = SanitizedError{strings.TrimSuffix(proc.Stderr, "\n")}
109 | }
110 | }
111 |
112 | return out, err
113 | }
114 |
115 | // MARK: Helpers
116 |
117 | // Show returns the requested information for the commit pointed to by HEAD.
118 | func (git *Git) Show(spec string) (string, error) {
119 | return git.ShowRef(spec, "HEAD")
120 | }
121 |
122 | // ShowRef returns the requested information for the given ref.
123 | func (git *Git) ShowRef(spec, ref string) (string, error) {
124 | return git.SanitizeProcess(git.Run("show", fmt.Sprintf("--format=%s", spec), ref, "--quiet"))
125 | }
126 |
127 | // ExtractRepoFromConfig gets the repo name from the Git config.
128 | func (git *Git) ExtractRepoFromConfig() (result Repo, err error) {
129 | if !git.IsRepo() {
130 | return result, ErrNotRepository{git.CurrentDirectory()}
131 | }
132 |
133 | proc, err := git.Run("config", "--get", "remote.origin.url")
134 | if err != nil {
135 | return result, ErrNoRemoteOrigin
136 | }
137 |
138 | return ExtractRepoFromURL(proc.Stdout), nil
139 | }
140 |
141 | // ExtractRepoFromURL gets the repo name from the remote URL.
142 | func ExtractRepoFromURL(s string) Repo {
143 | // removes the .git suffix and any new lines
144 | s = strings.NewReplacer(
145 | ".git", "",
146 | "\n", "",
147 | ).Replace(s)
148 | // if the URL contains a :, indicating a SSH config,
149 | // remove all chars until it, including itself
150 | // on HTTP and HTTPS URLs it will remove the http(s): prefix,
151 | // which is ok. On SSH URLs the whole user@server will be removed,
152 | // which is required.
153 | s = s[strings.LastIndex(s, ":")+1:]
154 | // split by /, the last to parts should be the owner and name
155 | ss := strings.Split(s, "/")
156 |
157 | return Repo{
158 | Owner: ss[len(ss)-2],
159 | Name: ss[len(ss)-1],
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/internal/git/git_test.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | package git
22 |
23 | import (
24 | "testing"
25 |
26 | "github.com/cidertool/cider/internal/shell"
27 | "github.com/cidertool/cider/internal/shell/shelltest"
28 | "github.com/cidertool/cider/pkg/config"
29 | "github.com/cidertool/cider/pkg/context"
30 | "github.com/stretchr/testify/assert"
31 | )
32 |
33 | func newMockGit(t *testing.T, commands ...shelltest.Command) *Git {
34 | t.Helper()
35 |
36 | ctx := context.New(config.Project{})
37 |
38 | return newMockGitWithContext(t, ctx, commands...)
39 | }
40 |
41 | func newMockGitWithContext(t *testing.T, ctx *context.Context, commands ...shelltest.Command) *Git {
42 | t.Helper()
43 |
44 | return &Git{
45 | Shell: &shelltest.Shell{
46 | T: t,
47 | Context: ctx,
48 | Commands: commands,
49 | },
50 | }
51 | }
52 |
53 | func TestNew(t *testing.T) {
54 | t.Parallel()
55 |
56 | ctx := context.New(config.Project{})
57 |
58 | client := New(ctx)
59 | ok := client.IsRepo()
60 | assert.True(t, ok)
61 | }
62 |
63 | func TestSanitizeProcess(t *testing.T) {
64 | t.Parallel()
65 |
66 | runFunc := func(client *Git) (*shell.CompletedProcess, error) {
67 | return client.RunInEnv(map[string]string{
68 | "TEST": "TEST",
69 | }, "test")
70 | }
71 |
72 | ctx := context.New(config.Project{})
73 |
74 | ctx.CurrentDirectory = "test"
75 | client := newMockGitWithContext(
76 | t,
77 | ctx,
78 | shelltest.Command{Stdout: "true", Stderr: "false"},
79 | shelltest.Command{ReturnCode: 1, Stdout: "true", Stderr: "false"},
80 | )
81 |
82 | // Test out sanitize
83 | proc, err := runFunc(client)
84 | assert.NoError(t, err)
85 | assert.Equal(t, []string{"git", "-c", "log.showSignature=false", "-C", "test", "test"}, proc.Args)
86 | out, err := client.SanitizeProcess(proc, err)
87 | assert.Equal(t, "true", out)
88 | assert.NoError(t, err)
89 |
90 | // Test error sanitize
91 | proc, err = runFunc(client)
92 | assert.Error(t, err)
93 | out, err = client.SanitizeProcess(proc, err)
94 | assert.Equal(t, "true", out)
95 | assert.Error(t, err)
96 | assert.EqualError(t, err, "false")
97 | }
98 |
99 | func TestShowRef(t *testing.T) {
100 | t.Parallel()
101 |
102 | // Selected the initial commit of this repo, because I needed a sha1 hash.
103 | expected := "eac16d260ebf8af83873c9704169cf40a5501f84"
104 | client := newMockGit(
105 | t,
106 | shelltest.Command{Stdout: expected},
107 | )
108 | got, err := client.Show("%H")
109 | assert.NoError(t, err)
110 | assert.Equal(t, expected, got)
111 | }
112 |
113 | func TestExtractRemoteFromConfig_Happy(t *testing.T) {
114 | t.Parallel()
115 |
116 | expected := Repo{
117 | Name: "cider",
118 | Owner: "cidertool",
119 | }
120 |
121 | client := newMockGit(
122 | t,
123 | shelltest.Command{Stdout: "true"},
124 | shelltest.Command{Stdout: "git@github.com:cidertool/cider.git"},
125 | )
126 | repo, err := client.ExtractRepoFromConfig()
127 | assert.NoError(t, err)
128 | assert.Equal(t, expected, repo)
129 | }
130 |
131 | func TestExtractRemoteFromConfig_ErrIsNotRepo(t *testing.T) {
132 | t.Parallel()
133 |
134 | client := newMockGit(
135 | t,
136 | shelltest.Command{Stdout: "false"},
137 | )
138 | repo, err := client.ExtractRepoFromConfig()
139 | assert.Error(t, err)
140 | assert.Empty(t, repo)
141 | }
142 |
143 | func TestExtractRemoteFromConfig_ErrNoRemoteNamedOrigin(t *testing.T) {
144 | t.Parallel()
145 |
146 | client := newMockGit(
147 | t,
148 | shelltest.Command{Stdout: "true"},
149 | shelltest.Command{ReturnCode: 1, Stderr: "no repo"},
150 | )
151 | repo, err := client.ExtractRepoFromConfig()
152 | assert.Error(t, err)
153 | assert.Empty(t, repo)
154 | }
155 |
156 | func TestExtractRepoFromURL(t *testing.T) {
157 | t.Parallel()
158 |
159 | var repo Repo
160 |
161 | expected := Repo{
162 | Name: "cider",
163 | Owner: "cidertool",
164 | }
165 | repo = ExtractRepoFromURL("https://github.com/cidertool/cider")
166 | assert.Equal(t, expected, repo)
167 | repo = ExtractRepoFromURL("https://github.com/cidertool/cider.git")
168 | assert.Equal(t, expected, repo)
169 | repo = ExtractRepoFromURL("ssh://github.com/cidertool/cider.git")
170 | assert.Equal(t, expected, repo)
171 | repo = ExtractRepoFromURL("ssh://git@github.com/cidertool/cider.git")
172 | assert.Equal(t, expected, repo)
173 | repo = ExtractRepoFromURL("git@github.com:cidertool/cider.git")
174 | assert.Equal(t, expected, repo)
175 | }
176 |
--------------------------------------------------------------------------------
/internal/pipe/testflight/testflight.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | // Package testflight is a pipe that processes an app's release to Testflight
22 | package testflight
23 |
24 | import (
25 | "fmt"
26 |
27 | "github.com/cidertool/asc-go/asc"
28 | "github.com/cidertool/cider/internal/client"
29 | "github.com/cidertool/cider/internal/log"
30 | "github.com/cidertool/cider/internal/pipe"
31 | "github.com/cidertool/cider/pkg/config"
32 | "github.com/cidertool/cider/pkg/context"
33 | )
34 |
35 | // Pipe is a global hook pipe.
36 | type Pipe struct {
37 | Client client.Client
38 | }
39 |
40 | // String is the name of this pipe.
41 | func (Pipe) String() string {
42 | return "committing to testflight"
43 | }
44 |
45 | // Publish to Testflight.
46 | func (p *Pipe) Publish(ctx *context.Context) error {
47 | if p.Client == nil {
48 | p.Client = client.New(ctx)
49 | }
50 |
51 | for _, name := range ctx.AppsToRelease {
52 | app, ok := ctx.Config[name]
53 | if !ok {
54 | return pipe.ErrMissingApp{Name: name}
55 | }
56 |
57 | ctx.Log.WithField("name", name).Info("preparing")
58 |
59 | err := p.doRelease(ctx, app)
60 | if err != nil {
61 | return err
62 | }
63 | }
64 |
65 | return nil
66 | }
67 |
68 | func (p *Pipe) doRelease(ctx *context.Context, config config.App) error {
69 | app, err := p.Client.GetAppForBundleID(ctx, config.BundleID)
70 | if err != nil {
71 | return err
72 | }
73 |
74 | build, err := p.Client.GetBuild(ctx, app)
75 | if err != nil {
76 | return err
77 | }
78 |
79 | buildVersionLog := fmt.Sprintf("%s (%s)", ctx.Version, *build.Attributes.Version)
80 |
81 | ctx.Log.WithFields(log.Fields{
82 | "app": *app.Attributes.BundleID,
83 | "build": buildVersionLog,
84 | }).Info("found resources")
85 |
86 | if ctx.SkipUpdateMetadata {
87 | ctx.Log.Warn("skipping updating metdata")
88 | } else {
89 | ctx.Log.Info("updating metadata")
90 | if err := p.updateBetaDetails(ctx, config, app, build); err != nil {
91 | return err
92 | }
93 | }
94 |
95 | if !ctx.SkipUpdateMetadata || ctx.OverrideBetaGroups {
96 | if err := p.updateBetaGroups(ctx, config, app, build); err != nil {
97 | return err
98 | }
99 | }
100 |
101 | if !ctx.SkipUpdateMetadata || ctx.OverrideBetaTesters {
102 | if err := p.updateBetaTesters(ctx, config, app, build); err != nil {
103 | return err
104 | }
105 | }
106 |
107 | if ctx.SkipSubmit {
108 | return pipe.ErrSkipSubmitEnabled
109 | }
110 |
111 | ctx.Log.
112 | WithField("build", buildVersionLog).
113 | Info("submitting to testflight")
114 |
115 | return p.Client.SubmitBetaApp(ctx, build.ID)
116 | }
117 |
118 | func (p *Pipe) updateBetaDetails(ctx *context.Context, config config.App, app *asc.App, build *asc.Build) error {
119 | ctx.Log.Infof("updating %d beta app localizations", len(config.Testflight.Localizations))
120 |
121 | if err := p.Client.UpdateBetaAppLocalizations(ctx, app.ID, config.Testflight.Localizations); err != nil {
122 | return err
123 | }
124 |
125 | ctx.Log.Info("updating beta build details")
126 |
127 | if err := p.Client.UpdateBetaBuildDetails(ctx, build.ID, config.Testflight); err != nil {
128 | return err
129 | }
130 |
131 | ctx.Log.Infof("updating %d beta build localizations", len(config.Testflight.Localizations))
132 |
133 | if err := p.Client.UpdateBetaBuildLocalizations(ctx, build.ID, config.Testflight.Localizations); err != nil {
134 | return err
135 | }
136 |
137 | ctx.Log.Info("updating beta license agreement")
138 |
139 | if err := p.Client.UpdateBetaLicenseAgreement(ctx, app.ID, config.Testflight); err != nil {
140 | return err
141 | }
142 |
143 | if config.Testflight.ReviewDetails != nil {
144 | ctx.Log.Info("updating beta review details")
145 |
146 | if err := p.Client.UpdateBetaReviewDetails(ctx, app.ID, *config.Testflight.ReviewDetails); err != nil {
147 | return err
148 | }
149 | }
150 |
151 | return nil
152 | }
153 |
154 | func (p *Pipe) updateBetaGroups(ctx *context.Context, config config.App, app *asc.App, build *asc.Build) error {
155 | ctx.Log.Info("updating build beta groups")
156 |
157 | return p.Client.AssignBetaGroups(ctx, app.ID, build.ID, config.Testflight.BetaGroups)
158 | }
159 |
160 | func (p *Pipe) updateBetaTesters(ctx *context.Context, config config.App, app *asc.App, build *asc.Build) error {
161 | ctx.Log.Info("updating build beta testers")
162 |
163 | return p.Client.AssignBetaTesters(ctx, app.ID, build.ID, config.Testflight.BetaTesters)
164 | }
165 |
--------------------------------------------------------------------------------
/docs/commands/cider_release.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | parent: Commands
4 | title: release
5 | nav_order: 2
6 | nav_exclude: false
7 | ---
8 |
9 | ## cider release
10 |
11 | Release the selected apps in the current project
12 |
13 | ### Synopsis
14 |
15 | Release the selected apps in the current project.
16 |
17 | You can provide a path to a project directory as an argument to be the root directory
18 | of all relative path expansions in the program, such as the Git repository, preview sets,
19 | and screenshot resources. The only exception to this is if you provide a custom configuration
20 | file path with the `--config` flag. Instead, that file will be loaded relative to
21 | the working directory of the Cider process itself.
22 |
23 | Additionally, Cider requires a few environment variables to be set in order to operate.
24 | They each correspond to an element of authorization described by the Apple Developer Documentation.
25 |
26 | - `ASC_KEY_ID`: The key's ID.
27 | - `ASC_ISSUER_ID`: Your team's issuer ID.
28 | - `ASC_PRIVATE_KEY` or `ASC_PRIVATE_KEY_PATH`: The .p8 private key issued by Apple. Must belong to an App Manager, Admin, or Account Holder.
29 |
30 | These three values each have varying degrees of sensetivity and should be treated as secrets. Store
31 | them securely in your environment so Cider can leverage them safely.
32 |
33 | More info: https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api
34 |
35 | ```
36 | cider release [path] [flags]
37 | ```
38 |
39 | ### Examples
40 |
41 | ```
42 | cider release --mode=appstore --set-version="1.0"
43 | ```
44 |
45 | ### Options
46 |
47 | ```
48 | -A, --all-apps --app Process all apps in the configuration file. Supercedes any usage of the --app flag.
49 | -a, --app stringArray Process the given app, providing the app key name used in your configuration file.
50 |
51 | This flag can be provided repeatedly for each app you want to process. You can omit
52 | this flag if your configuration file has only one app defined.
53 | -f, --config string Load configuration from file
54 | -h, --help help for release
55 | -p, --max-processes int Run certain metadata syncing and asset uploading logic in parallel with
56 | the maximum allowable concurrency. (default 1)
57 | --mode {appstore,testflight} Mode used to declare the publishing target for submission.
58 |
59 | The default is "testflight" for submitting to Testflight, and the other alternative
60 | option is "appstore" for submitting to the App Store.
61 | --set-beta-group stringArray Provide names of beta groups to release to instead of using
62 | the configuration file.
63 | --set-beta-tester stringArray Provide email addresses of beta testers to release to instead of
64 | using the configuration file.
65 | -B, --set-build string Build override to use instead of "latest". Corresponds to the CFBundleVersion
66 | of your build.
67 |
68 | The default behavior without this flag is to select the latest build. In both cases,
69 | if the selected build has an invalid processing state, Cider will abort with an error
70 | to ensure your release is handled safely.
71 | -V, --set-version string Version string override to use instead of parsing Git tags. Corresponds to the
72 | CFBundleShortVersionString of your build.
73 |
74 | Cider expects this string to follow the Major.Minor.Patch semantics outlined in Apple documentation
75 | and Semantic Versioning (semver). If this flag is omitted, Git will be leveraged to determine the
76 | latest tag. The tag will be used to calculate the version string under the same constraints.
77 | --skip-git --set-version Skips deriving version information from Git. Must only be used in conjunction with the --set-version flag.
78 | --skip-submit Skips submitting for review
79 | --skip-update-metadata Skips updating metadata (app info, localizations, assets, review details, etc.)
80 | --skip-update-pricing Skips updating app pricing
81 | --timeout duration Timeout for the entire release process.
82 |
83 | If the command takes longer than this amount of time to run, Cider will abort. (default 30m0s)
84 | ```
85 |
86 | ### Options inherited from parent commands
87 |
88 | ```
89 | --debug Enable debug mode
90 | ```
91 |
92 | ### SEE ALSO
93 |
94 | * [cider](/commands/cider/) - Submit your builds to the Apple App Store in seconds
95 |
96 |
--------------------------------------------------------------------------------
/pkg/context/context.go:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (C) 2020 Aaron Sky.
3 |
4 | This file is part of Cider, a tool for automating submission
5 | of apps to Apple's App Stores.
6 |
7 | Cider is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | Cider is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with Cider. If not, see .
19 | */
20 |
21 | // Package context manages the state of the pipeline
22 | package context
23 |
24 | import (
25 | ctx "context"
26 | "fmt"
27 | "os"
28 | "strings"
29 | "time"
30 |
31 | "github.com/cidertool/cider/internal/log"
32 | "github.com/cidertool/cider/pkg/config"
33 | )
34 |
35 | // PublishMode describes which review destination to publish to.
36 | type PublishMode string
37 |
38 | const (
39 | // PublishModeTestflight publishes to Testflight via beta app review.
40 | PublishModeTestflight PublishMode = "testflight"
41 | // PublishModeAppStore publishes for App Store review.
42 | PublishModeAppStore PublishMode = "appstore"
43 | )
44 |
45 | type errInvalidPublishMode struct {
46 | Value string
47 | }
48 |
49 | func (e errInvalidPublishMode) Error() string {
50 | return fmt.Sprintf("invalid value %s for publish mode", e.Value)
51 | }
52 |
53 | // Context carries along some data through the pipes.
54 | type Context struct {
55 | ctx.Context
56 | Config config.Project
57 | RawConfig config.Project
58 | Env Env
59 | Date time.Time
60 | Git GitInfo
61 | CurrentDirectory string
62 | Credentials Credentials
63 | AppsToRelease []string
64 | PublishMode PublishMode
65 | Log log.Interface
66 | MaxProcesses int
67 | SkipGit bool
68 | SkipUpdatePricing bool
69 | SkipUpdateMetadata bool
70 | SkipSubmit bool
71 | OverrideBetaGroups bool
72 | OverrideBetaTesters bool
73 | VersionIsInitialRelease bool
74 | Version string
75 | Build string
76 | Semver Semver
77 | }
78 |
79 | // Env is the environment variables.
80 | type Env map[string]string
81 |
82 | // GitInfo includes tags and refs.
83 | type GitInfo struct {
84 | CurrentTag string
85 | Commit string
86 | ShortCommit string
87 | FullCommit string
88 | CommitDate time.Time
89 | URL string
90 | }
91 |
92 | // Semver represents a semantic version.
93 | type Semver struct {
94 | Major uint64
95 | Minor uint64
96 | Patch uint64
97 | Prerelease string
98 | RawVersion string
99 | }
100 |
101 | // New context.
102 | func New(config config.Project) *Context {
103 | return Wrap(ctx.Background(), config)
104 | }
105 |
106 | // NewWithTimeout new context with the given timeout.
107 | func NewWithTimeout(config config.Project, timeout time.Duration) (*Context, ctx.CancelFunc) {
108 | ctx, cancel := ctx.WithTimeout(ctx.Background(), timeout)
109 |
110 | return Wrap(ctx, config), cancel
111 | }
112 |
113 | // Wrap wraps an existing context.
114 | func Wrap(ctx ctx.Context, config config.Project) *Context {
115 | return &Context{
116 | Context: ctx,
117 | Config: config,
118 | RawConfig: config,
119 | Env: splitEnv(os.Environ()),
120 | Date: time.Now(),
121 | Log: log.New(),
122 | MaxProcesses: 1,
123 | }
124 | }
125 |
126 | // Copy returns a copy of the environment.
127 | func (e Env) Copy() Env {
128 | var out = Env{}
129 | for k, v := range e {
130 | out[k] = v
131 | }
132 |
133 | return out
134 | }
135 |
136 | // Strings returns the current environment as a list of strings, suitable for
137 | // os executions.
138 | func (e Env) Strings() []string {
139 | var result = make([]string, 0, len(e))
140 | for k, v := range e {
141 | result = append(result, k+"="+v)
142 | }
143 |
144 | return result
145 | }
146 |
147 | func splitEnv(env []string) map[string]string {
148 | r := map[string]string{}
149 |
150 | for _, e := range env {
151 | p := strings.SplitN(e, "=", 2)
152 | r[p[0]] = p[1]
153 | }
154 |
155 | return r
156 | }
157 |
158 | // String returns the string value of the mode.
159 | func (m PublishMode) String() string {
160 | return string(m)
161 | }
162 |
163 | // Set the mode to an allowed value, or return an error.
164 | func (m *PublishMode) Set(value string) error {
165 | switch value {
166 | case "appstore":
167 | *m = PublishModeAppStore
168 |
169 | return nil
170 | case "testflight":
171 | *m = PublishModeTestflight
172 |
173 | return nil
174 | }
175 |
176 | return errInvalidPublishMode{Value: value}
177 | }
178 |
179 | // Type returns a representation of permissible values.
180 | func (m PublishMode) Type() string {
181 | return "{appstore,testflight}"
182 | }
183 |
--------------------------------------------------------------------------------