Skip to content

Commit 97249a1

Browse files
committed
chore: Enable automation testing
Signed-off-by: Steve Hipwell <steve.hipwell@gmail.com>
1 parent 1af72d4 commit 97249a1

10 files changed

Lines changed: 421 additions & 315 deletions

.github/workflows/dotcom-acceptance-tests.yaml

Lines changed: 120 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ name: Acceptance Tests (github.com)
22

33
on:
44
workflow_dispatch:
5-
# push:
6-
# branches:
7-
# - main
8-
# - release-v*
5+
push:
6+
branches:
7+
- main
8+
- release-v*
9+
# pull_request_target:
910
pull_request:
1011
types:
1112
- opened
@@ -23,17 +24,70 @@ concurrency:
2324
permissions: read-all
2425

2526
jobs:
27+
setup:
28+
name: Setup
29+
runs-on: ubuntu-latest
30+
defaults:
31+
run:
32+
shell: bash
33+
outputs:
34+
fork: ${{ steps.check.outputs.fork }}
35+
test: ${{ steps.check.outputs.test }}
36+
environment: ${{ steps.check.outputs.environment }}
37+
steps:
38+
- name: Check
39+
id: check
40+
env:
41+
GITHUB_HEAD_REPO: ${{ case(github.event_name == 'pull_request' || github.event_name == 'pull_request_target', github.event.pull_request.head.repo.full_name, github.repository) }}
42+
GITHUB_BASE_REPO: ${{ case(github.event_name == 'pull_request' || github.event_name == 'pull_request_target', github.event.pull_request.base.repo.full_name, github.repository) }}
43+
ACCTEST_LABEL_SET: ${{ contains(github.event.pull_request.labels.*.name, 'acctest') }}
44+
run: |
45+
set -euo pipefail
46+
47+
fork="true"
48+
test="false"
49+
environment="acctest-dotcom-untrusted"
50+
51+
if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]] || [[ "${GITHUB_EVENT_NAME}" == "push" ]]; then
52+
fork="false"
53+
test="true"
54+
environment="acctest-dotcom"
55+
echo "::notice::Running in ${GITHUB_EVENT_NAME} context, proceeding with tests"
56+
else
57+
if [[ "${GITHUB_HEAD_REPO}" == "${GITHUB_BASE_REPO}" ]]; then
58+
fork="false"
59+
test="true"
60+
environment="acctest-dotcom"
61+
echo "::notice::Running in ${GITHUB_EVENT_NAME} context from the base repository, proceeding with tests"
62+
else
63+
if [[ "${ACCTEST_LABEL_SET}" == "true" ]]; then
64+
test="true"
65+
echo "::warning::Running in ${GITHUB_EVENT_NAME} context from a fork, proceeding with tests as acctest label is set"
66+
else
67+
echo "::warning::Running in ${GITHUB_EVENT_NAME} context from a fork, skipping tests as acctest label is not set"
68+
fi
69+
fi
70+
fi
71+
72+
{
73+
echo "test=${test}"
74+
echo "environment=${environment}"
75+
echo "fork=${fork}"
76+
} >> "${GITHUB_OUTPUT}"
77+
2678
test:
27-
name: Test ${{ matrix.mode }}
28-
if: (github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || contains(github.event.pull_request.labels.*.name, 'acctest')
79+
name: Test ${{ matrix.mode || 'Skipped' }}
80+
needs:
81+
- setup
82+
if: needs.setup.outputs.test == 'true'
2983
runs-on: ubuntu-latest
3084
permissions:
3185
contents: read
3286
environment:
33-
name: acctest-dotcom
87+
name: ${{ needs.setup.outputs.environment }}
3488
strategy:
3589
matrix:
36-
mode: [anonymous, individual, organization] # team, enterprise
90+
mode: [organization] # anonymous, individual, team, enterprise
3791
fail-fast: true
3892
max-parallel: 1
3993
defaults:
@@ -44,33 +98,75 @@ jobs:
4498
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
4599

46100
- name: Check secrets
47-
if: github.event_name == 'pull_request_target'
101+
if: github.event_name == 'pull_request' || github.event_name == 'pull_request_target'
48102
env:
49-
INPUT_ALLOWED_SECRETS: ${{ vars.DOTCOM_ACCEPTANCE_TESTS_ALLOWED_SECRETS || 'GH_TEST_TOKEN' }}
50103
INPUT_SECRETS: ${{ toJSON(secrets) }}
104+
INPUT_ALLOWED_SECRETS: ${{ vars.GH_TEST_ALLOWED_SECRETS }}
51105
run: |
52106
set -eou pipefail
53107
54-
secret_keys="$(jq --raw-output --compact-output '[. | keys[] | select(test("^(?:(?:ACTIONS)|(?:actions)|(?:GITHUB)|(?:github)|(?:TEST)|(?:test))_") | not)] | sort | join(",")' <<<"${INPUT_SECRETS}")"
55-
if [[ "${secret_keys}" != "${INPUT_ALLOWED_SECRETS}" ]]; then
56-
echo "::error::Too many or too few secrets configured: ${secret_keys}"
108+
allowed_secrets="$(jq --raw-input --raw-output --compact-output 'split(",")' <<<"${INPUT_ALLOWED_SECRETS}")"
109+
110+
secret_keys="$(jq --raw-output --compact-output --argjson allowed "${allowed_secrets}" '[[. | to_entries[] | select(.value != "" and .value != "!NOSECRET!")] | from_entries | keys[] | ascii_upcase | select(test("^(?:(?:ACTIONS)|(?:GITHUB)|(?:TEST)|(?:GH_TEST))_") | not) | select((IN($allowed[]) | not))] | sort | join(",")' <<<"${INPUT_SECRETS}")"
111+
if [[ -n "${secret_keys}" ]]; then
112+
echo "::error::Unexpected secrets: ${secret_keys}"
57113
exit 1
58114
fi
59115
60116
- name: Check credentials
61117
id: credentials
62118
if: matrix.mode != 'anonymous'
63119
env:
120+
MATRIX_MODE: ${{ matrix.mode }}
121+
GH_TEST_APP_ID: ${{ vars.GH_TEST_APP_ID }}
122+
GH_TEST_APP_INSTALLATION_ID: ${{ vars.GH_TEST_APP_INSTALLATION_ID }}
123+
GH_TEST_APP_PEM: ${{ secrets.GH_TEST_APP_PEM }}
64124
GH_TEST_TOKEN: ${{ secrets.GH_TEST_TOKEN }}
65125
run: |
66126
set -eou pipefail
67127
68-
if [[ -z "${GH_TEST_TOKEN}" ]]; then
69-
echo "::error::Missing credentials"
70-
exit 1
128+
app_id=""
129+
app_installation_id=""
130+
app_pem=""
131+
token=""
132+
133+
if [[ "${MATRIX_MODE}" == "individual" ]]; then
134+
if [[ -z "${GH_TEST_TOKEN}" ]]; then
135+
echo "::error::Missing token"
136+
exit 1
137+
fi
138+
139+
token="${GH_TEST_TOKEN}"
140+
else
141+
if [[ -z "${GH_TEST_APP_ID}" ]]; then
142+
echo "::error::Missing app id"
143+
exit 1
144+
fi
145+
146+
if [[ -z "${GH_TEST_APP_INSTALLATION_ID}" ]]; then
147+
echo "::error::Missing app installation id"
148+
exit 1
149+
fi
150+
151+
if [[ -z "${GH_TEST_APP_PEM}" ]]; then
152+
echo "::error::Missing app pem"
153+
exit 1
154+
fi
155+
156+
app_id="${GH_TEST_APP_ID}"
157+
app_installation_id="${GH_TEST_APP_INSTALLATION_ID}"
158+
app_pem="${GH_TEST_APP_PEM}"
71159
fi
72160
73-
echo "token=${GH_TEST_TOKEN}" >> "${GITHUB_OUTPUT}"
161+
{
162+
echo "app_id=${app_id}"
163+
echo "app_installation_id=${app_installation_id}"
164+
printf 'app_pem<<EOF
165+
%s
166+
EOF
167+
' "${app_pem}"
168+
echo "token=${token}"
169+
} >> "${GITHUB_OUTPUT}"
74170
75171
- name: Set-up Go
76172
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
@@ -101,11 +197,14 @@ jobs:
101197
TF_ACC_TERRAFORM_PATH: ${{ steps.tf.outputs.path }}
102198
TF_ACC: "1"
103199
TF_LOG: WARN
200+
GITHUB_APP_ID: ${{ steps.credentials.outputs.app_id }}
201+
GITHUB_APP_INSTALLATION_ID: ${{ steps.credentials.outputs.app_installation_id }}
202+
GITHUB_APP_PEM_FILE: ${{ steps.credentials.outputs.app_pem }}
104203
GITHUB_TOKEN: ${{ steps.credentials.outputs.token }}
105204
GITHUB_BASE_URL: https://api.github.com/
106-
GITHUB_OWNER: ${{ (matrix.mode == 'individual' && vars.GH_TEST_LOGIN) || (matrix.mode == 'organization' && vars.GH_TEST_ORG_NAME) || '' }}
107-
GITHUB_USERNAME: ${{ vars.GH_TEST_LOGIN }}
108-
GITHUB_ENTERPRISE_SLUG: ${{ vars.GH_TEST_ENTERPRISE_SLUG }}
205+
GITHUB_OWNER: ${{ case(matrix.mode == 'anonymous', '', matrix.mode == 'individual', vars.GH_TEST_LOGIN, vars.GH_TEST_ORG_NAME) }}
206+
GITHUB_USERNAME: ${{ case(matrix.mode == 'individual', vars.GH_TEST_LOGIN, '') }}
207+
GITHUB_ENTERPRISE_SLUG: ${{ case(matrix.mode == 'enterprise', vars.GH_TEST_ENTERPRISE_SLUG, '') }}
109208
GH_TEST_AUTH_MODE: ${{ matrix.mode }}
110209
GH_TEST_USER_REPOSITORY: ${{ vars.GH_TEST_USER_REPOSITORY }}
111210
GH_TEST_ORG_USER: ${{ vars.GH_TEST_ORG_USER }}
@@ -128,7 +227,7 @@ jobs:
128227
129228
check:
130229
name: Check DotCom Acceptance Tests
131-
if: always() && github.event_name == 'pull_request'
230+
if: always() && (github.event_name == 'pull_request' || github.event_name == 'pull_request_target')
132231
needs:
133232
- test
134233
runs-on: ubuntu-latest

.github/workflows/ghes-acceptance-tests.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ name: Acceptance Tests (GHES)
22

33
on:
44
workflow_dispatch:
5+
# push:
6+
# branches:
7+
# - main
8+
# - release-v*
59
# pull_request_target:
610
# types:
711
# - opened

github/acc_test.go

Lines changed: 76 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,13 @@ type testAccConfig struct {
3737
baseURL *url.URL
3838

3939
// Auth configuration
40-
authMode testMode
41-
owner string
42-
username string
43-
token string
40+
authMode testMode
41+
owner string
42+
username string
43+
token string
44+
appID string
45+
appInstallationID string
46+
appPEM string
4447

4548
// Enterprise configuration
4649
enterpriseSlug string
@@ -112,13 +115,11 @@ func TestMain(m *testing.M) {
112115
}
113116

114117
config := testAccConfig{
115-
baseURL: baseURL,
116-
authMode: authMode,
117-
testPublicRepository: "terraform-provider-github",
118-
testPublicRepositoryOwner: "integrations",
119-
testPublicReleaseId: 186531906,
120-
// The terraform-provider-github_6.4.0_manifest.json asset ID from
121-
// https://github.com/integrations/terraform-provider-github/releases/tag/v6.4.0
118+
baseURL: baseURL,
119+
authMode: authMode,
120+
testPublicRepository: "terraform-provider-github",
121+
testPublicRepositoryOwner: "integrations",
122+
testPublicReleaseId: 186531906, // The terraform-provider-github_6.4.0_manifest.json asset ID from https://github.com/integrations/terraform-provider-github/releases/tag/v6.4.0
122123
testPublicRelaseAssetId: "207956097",
123124
testPublicRelaseAssetName: "terraform-provider-github_6.4.0_manifest.json",
124125
testPublicReleaseAssetContent: "{\n \"version\": 1,\n \"metadata\": {\n \"protocol_versions\": [\n \"5.0\"\n ]\n }\n}",
@@ -135,30 +136,38 @@ func TestMain(m *testing.M) {
135136
testExternalUser2: os.Getenv("GH_TEST_EXTERNAL_USER2"),
136137
testAdvancedSecurity: os.Getenv("GH_TEST_ADVANCED_SECURITY") == "true",
137138
testRepositoryVisibility: "public",
138-
enterpriseIsEMU: authMode == enterprise && os.Getenv("GH_TEST_ENTERPRISE_IS_EMU") == "true",
139139
}
140140

141141
if config.authMode != anonymous {
142142
config.owner = os.Getenv("GITHUB_OWNER")
143143
config.username = os.Getenv("GITHUB_USERNAME")
144144
config.token = os.Getenv("GITHUB_TOKEN")
145+
config.appID = os.Getenv("GITHUB_APP_ID")
146+
config.appInstallationID = os.Getenv("GITHUB_APP_INSTALLATION_ID")
147+
config.appPEM = os.Getenv("GITHUB_APP_PEM_FILE")
145148

146149
if len(config.owner) == 0 {
147150
fmt.Println("GITHUB_OWNER environment variable not set")
148151
os.Exit(1)
149152
}
150153

151-
if len(config.username) == 0 {
154+
if config.authMode == individual && len(config.username) == 0 {
152155
fmt.Println("GITHUB_USERNAME environment variable not set")
153156
os.Exit(1)
154157
}
155158

156-
if len(config.token) == 0 {
157-
fmt.Println("GITHUB_TOKEN environment variable not set")
159+
if len(config.token) == 0 && (len(config.appID) == 0 || len(config.appInstallationID) == 0 || len(config.appPEM) == 0) {
160+
fmt.Println("authentication not configured")
158161
os.Exit(1)
159162
}
160163
}
161164

165+
if config.authMode != anonymous && config.authMode != individual {
166+
if i, err := strconv.Atoi(os.Getenv("GH_TEST_ORG_APP_INSTALLATION_ID")); err == nil {
167+
config.testOrgAppInstallationId = i
168+
}
169+
}
170+
162171
if config.authMode == enterprise {
163172
config.enterpriseSlug = os.Getenv("GITHUB_ENTERPRISE_SLUG")
164173

@@ -167,33 +176,57 @@ func TestMain(m *testing.M) {
167176
os.Exit(1)
168177
}
169178

170-
i, err := strconv.Atoi(os.Getenv("GH_TEST_ENTERPRISE_EMU_GROUP_ID"))
171-
if err == nil {
172-
config.testEnterpriseEMUGroupId = i
179+
if os.Getenv("GH_TEST_ENTERPRISE_IS_EMU") == "true" {
180+
config.enterpriseIsEMU = true
181+
182+
if i, err := strconv.Atoi(os.Getenv("GH_TEST_ENTERPRISE_EMU_GROUP_ID")); err == nil {
183+
config.testEnterpriseEMUGroupId = i
184+
}
173185
}
174186

175187
if config.enterpriseIsEMU {
176188
config.testRepositoryVisibility = "private"
177189
}
178190
}
179191

180-
i, err := strconv.Atoi(os.Getenv("GH_TEST_ORG_APP_INSTALLATION_ID"))
181-
if err == nil {
182-
config.testOrgAppInstallationId = i
183-
}
184-
185192
testAccConf = &config
186193

187194
configureSweepers()
188195

189196
resource.TestMain(m)
190197
}
191198

199+
func getTestAppToken() (string, error) {
200+
if testAccConf.appID == "" || testAccConf.appInstallationID == "" || testAccConf.appPEM == "" {
201+
return "", fmt.Errorf("app auth not configured")
202+
}
203+
204+
appToken, err := GenerateOAuthTokenFromApp(testAccConf.baseURL, testAccConf.appID, testAccConf.appInstallationID, testAccConf.appPEM)
205+
if err != nil {
206+
return "", err
207+
}
208+
209+
return appToken, nil
210+
}
211+
212+
func getTestToken() (string, error) {
213+
if testAccConf.token != "" {
214+
return testAccConf.token, nil
215+
}
216+
217+
return getTestAppToken()
218+
}
219+
192220
func getTestMeta() (*Owner, error) {
221+
token, err := getTestToken()
222+
if err != nil {
223+
return nil, fmt.Errorf("error getting test token: %w", err)
224+
}
225+
193226
config := Config{
194-
Token: testAccConf.token,
195-
Owner: testAccConf.owner,
196227
BaseURL: testAccConf.baseURL,
228+
Owner: testAccConf.owner,
229+
Token: token,
197230
}
198231

199232
meta, err := config.Meta()
@@ -292,6 +325,24 @@ func skipUnauthenticated(t *testing.T) {
292325
}
293326
}
294327

328+
func skipNoToken(t *testing.T) {
329+
if testAccConf.authMode == anonymous {
330+
t.Skip("Skipping as test mode not authenticated")
331+
}
332+
if testAccConf.token == "" {
333+
t.Skip("Skipping as no token provided")
334+
}
335+
}
336+
337+
func skipNoApp(t *testing.T) {
338+
if testAccConf.authMode == anonymous {
339+
t.Skip("Skipping as test mode not authenticated")
340+
}
341+
if testAccConf.appID == "" || testAccConf.appInstallationID == "" || testAccConf.appPEM == "" {
342+
t.Skip("Skipping as app not configured")
343+
}
344+
}
345+
295346
func skipUnlessHasOrgs(t *testing.T) {
296347
if !slices.Contains(orgTestModes, testAccConf.authMode) {
297348
t.Skip("Skipping as test mode doesn't have orgs")

0 commit comments

Comments
 (0)