Compare commits
26
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
552493bfa4 | ||
|
|
8decd6ca07 | ||
|
|
b4b91e9993 | ||
|
|
e30fad8050 | ||
|
|
d7e1ea845e | ||
|
|
51dff542d6 | ||
|
|
ef922b8e3b | ||
|
|
6d732db1cf | ||
|
|
86c578bdf2 | ||
|
|
621a30f411 | ||
|
|
0ae35c149f | ||
|
|
4f5ae77ae9 | ||
|
|
db79671e1d | ||
|
|
3a24d8ed61 | ||
|
|
4b5fb2c8ad | ||
|
|
37946dfc91 | ||
|
|
a7d588cf7c | ||
|
|
c32265a4df | ||
|
|
669526d239 | ||
|
|
e4525fb4aa | ||
|
|
7ef65dedf2 | ||
|
|
51405dbbd5 | ||
|
|
e59ec6da57 | ||
|
|
f123f251b9 | ||
|
|
1854c6ae2d | ||
|
|
400f484e15 |
@@ -0,0 +1,34 @@
|
|||||||
|
name: Actions Sync E2E Sanity Test Reusable
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
runson:
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
secrets:
|
||||||
|
ghes_url:
|
||||||
|
required: true
|
||||||
|
actions_sync_releasedatetime:
|
||||||
|
required: true
|
||||||
|
site_admin_token:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
execute:
|
||||||
|
runs-on: ${{ inputs.runson }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Bootstrap
|
||||||
|
run: |
|
||||||
|
.\script\bootstrap-sanity-test.ps1
|
||||||
|
shell: pwsh
|
||||||
|
env:
|
||||||
|
RELEASEDATE: ${{ secrets.actions_sync_releasedatetime }}
|
||||||
|
- name: Test execution
|
||||||
|
run: |
|
||||||
|
.\script\execute-sanity-test.ps1
|
||||||
|
shell: pwsh
|
||||||
|
env:
|
||||||
|
TOKEN: ${{ secrets.site_admin_token }}
|
||||||
|
TEST_INSTANCE_URL: ${{ secrets.ghes_url }}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
name: Actions Sync E2E Sanity Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sanity-test:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
runson: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
|
uses: ./.github/workflows/actions-sync-e2e-test-called.yml
|
||||||
|
with:
|
||||||
|
runson: ${{ matrix.runson }}
|
||||||
|
secrets:
|
||||||
|
ghes_url: ${{ secrets.sanity_test_ghes_url }}
|
||||||
|
actions_sync_releasedatetime: ${{ secrets.sanity_test_releasedatetime }}
|
||||||
|
site_admin_token: ${{ secrets.sanity_test_site_admin_token }}
|
||||||
|
|
||||||
+2
-1
@@ -2,4 +2,5 @@
|
|||||||
_tools/bin
|
_tools/bin
|
||||||
bin
|
bin
|
||||||
test/tmp
|
test/tmp
|
||||||
dist
|
dist
|
||||||
|
actions-sync
|
||||||
|
|||||||
@@ -35,7 +35,9 @@ When there are machines which have access to both the public internet and the GH
|
|||||||
- `repo-name-list` _(optional)_
|
- `repo-name-list` _(optional)_
|
||||||
A comma-separated list of repositories to be synced. Each entry follows the format of `repo-name`.
|
A comma-separated list of repositories to be synced. Each entry follows the format of `repo-name`.
|
||||||
- `repo-name-list-file` _(optional)_
|
- `repo-name-list-file` _(optional)_
|
||||||
A path to a file containing a newline separate listof repositories to be synced. Each entry follows te format of `repo-name`.
|
A path to a file containing a newline separated list of repositories to be synced. Each entry follows the format of `repo-name`.
|
||||||
|
- `actions-admin-user` _(optional)_
|
||||||
|
The name of the Actions admin user, which will be used for updating the chosen action. To use the default user, pass `actions-admin`. If not set, the impersonation is disabled. Note that `site_admin` scope is required in the token for the impersonation to work.
|
||||||
|
|
||||||
**Example Usage:**
|
**Example Usage:**
|
||||||
|
|
||||||
@@ -64,11 +66,11 @@ When no machine has access to both the public internet and the GHES instance:
|
|||||||
- `cache-dir` _(required)_
|
- `cache-dir` _(required)_
|
||||||
The directory to cache the pulled repositories into.
|
The directory to cache the pulled repositories into.
|
||||||
- `repo-name` _(optional)_
|
- `repo-name` _(optional)_
|
||||||
A single repository to be synced. In the format of `owner/repo`. Optionally if you wish the repository to be named different on your GHES instance you can provide an aliase in the format: `upstream_owner/up_streamrepo:destination_owner/destination_repo`
|
A single repository to be synced. In the format of `owner/repo`. Optionally if you wish the repository to be named different on your GHES instance you can provide an alias in the format: `upstream_owner/upstream_repo:destination_owner/destination_repo`
|
||||||
- `repo-name-list` _(optional)_
|
- `repo-name-list` _(optional)_
|
||||||
A comma-separated list of repositories to be synced. Each entry follows the format of `repo-name`.
|
A comma-separated list of repositories to be synced. Each entry follows the format of `repo-name`.
|
||||||
- `repo-name-list-file` _(optional)_
|
- `repo-name-list-file` _(optional)_
|
||||||
A path to a file containing a newline separate listof repositories to be synced. Each entry follows te format of `repo-name`.
|
A path to a file containing a newline separated list of repositories to be synced. Each entry follows the format of `repo-name`.
|
||||||
|
|
||||||
**Example Usage:**
|
**Example Usage:**
|
||||||
|
|
||||||
@@ -92,6 +94,8 @@ When no machine has access to both the public internet and the GHES instance:
|
|||||||
A personal access token to authenticate against the GHES instance when uploading repositories. See [Destination token scopes](#destination-token-scopes) below.
|
A personal access token to authenticate against the GHES instance when uploading repositories. See [Destination token scopes](#destination-token-scopes) below.
|
||||||
- `repo-name`, `repo-name-list` or `repo-name-list-file` _(optional)_
|
- `repo-name`, `repo-name-list` or `repo-name-list-file` _(optional)_
|
||||||
Limit push to specific repositories in the cache directory.
|
Limit push to specific repositories in the cache directory.
|
||||||
|
- `actions-admin-user` _(optional)_
|
||||||
|
The name of the Actions admin user, which will be used for updating the chosen action. To use the default user, pass `actions-admin`. If not set, the impersonation is disabled. Note that `site_admin` scope is required in the token for the impersonation to work.
|
||||||
|
|
||||||
**Example Usage:**
|
**Example Usage:**
|
||||||
|
|
||||||
@@ -104,7 +108,7 @@ When no machine has access to both the public internet and the GHES instance:
|
|||||||
|
|
||||||
## Destination token scopes
|
## Destination token scopes
|
||||||
|
|
||||||
When creating a personal access token include the `repo` and `workflow` scopes. Include the `site_admin` scope (optional) if you want organizations to be created as necessary.
|
When creating a personal access token include the `repo` and `workflow` scopes. Include the `site_admin` scope (optional) if you want organizations to be created as necessary or you want to use the impersonation logic for the `push` or `sync` commands.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# Release process
|
||||||
|
|
||||||
|
When we want to do a new release, push a git tag with format `v**` and workflow `releases.yml` executes.
|
||||||
|
|
||||||
|
This workflow internally uses [go-releaser](https://goreleaser.com/ci/actions/) to push a new release.
|
||||||
|
|
||||||
|
Please follow the below detailed steps.
|
||||||
|
- Create a tag in format `v202205240715`
|
||||||
|
```code
|
||||||
|
git tag -a `date "+v%Y%m%d%H%M"` -m "Release a new version"
|
||||||
|
```
|
||||||
|
- Get the tag name
|
||||||
|
```code
|
||||||
|
git tag
|
||||||
|
```
|
||||||
|
- Push the newly created tag
|
||||||
|
```code
|
||||||
|
git push origin <tag>
|
||||||
|
```
|
||||||
|
- Check that workflow [`releases.yml`](https://github.com/actions/actions-sync/actions/workflows/releases.yml) was triggered
|
||||||
|
- Once completed, [go to repo releases page](https://github.com/actions/actions-sync/releases) and edit the newly created release as `pre-release`, so we can do sanity testing before we officially release
|
||||||
|
- Recommend to do basic sanity testing (see below) on the new release.
|
||||||
|
- Once sanity testing is done, we can edit the release and mark it as `Latest version` and edit the release notes.
|
||||||
|
|
||||||
|
## Basic Sanity testing
|
||||||
|
|
||||||
|
### Prerequisite
|
||||||
|
|
||||||
|
1. Access to a GHES test server
|
||||||
|
1. Create a PAT token with `site-admin` scope in the GHES environment for `ghe-admin`
|
||||||
|
|
||||||
|
### Execution
|
||||||
|
|
||||||
|
1. Update below Repository level secrets:
|
||||||
|
|
||||||
|
- sanity_test_site_admin_token: The PAT generated earlier
|
||||||
|
- sanity_test_ghes_url: The URL to the GHES instance
|
||||||
|
- sanity_test_releasedatetime: The tag datetime string for the release to test without the `v` (e.g. `202211070205`)
|
||||||
|
|
||||||
|
1. Manually trigger this workflow: https://github.com/actions/actions-sync/actions/workflows/actions-sync-e2e-test-caller.yml
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Determine file to download based on current OS
|
||||||
|
if($IsLinux) {
|
||||||
|
$file_postfix = "linux_amd64"
|
||||||
|
} elseif ($IsWindows) {
|
||||||
|
$file_postfix = "windows_amd64"
|
||||||
|
} elseif ($IsMacOS) {
|
||||||
|
$file_postfix = "darwin_amd64"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Download release to test
|
||||||
|
curl -OL "https://github.com/actions/actions-sync/releases/download/v$Env:RELEASEDATE/gh_$Env:RELEASEDATE`_$file_postfix.tar.gz"
|
||||||
|
|
||||||
|
# extract
|
||||||
|
tar -xvzf "gh_$Env:RELEASEDATE`_$file_postfix.tar.gz"
|
||||||
|
|
||||||
|
# prepare cache directory
|
||||||
|
mkdir -p cache
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# Testing Pull Single Repo
|
||||||
|
echo "`n#########################`n### Testing Pull Single Repo`n#########################"
|
||||||
|
bin/actions-sync pull --cache-dir "cache" --repo-name "actions/setup-node"
|
||||||
|
|
||||||
|
# Testing Sync Single Repo
|
||||||
|
echo "`n#########################`n### Testing Sync Single Repo`n#########################"
|
||||||
|
bin/actions-sync sync --cache-dir "cache" --destination-token $Env:TOKEN --destination-url $Env:TEST_INSTANCE_URL --repo-name "actions/setup-node" --actions-admin-user actions-admin
|
||||||
|
|
||||||
|
# Testing Pull Multiple Repos
|
||||||
|
echo "`n#########################`n### Testing Pull Multiple Repos`n#########################"
|
||||||
|
bin/actions-sync pull --cache-dir "cache" --repo-name-list "actions/setup-node,actions/checkout"
|
||||||
|
|
||||||
|
# Testing Push Multiple Existing Repos
|
||||||
|
echo "`n#########################`n### Testing Push Multiple Existing Repos`n#########################"
|
||||||
|
bin/actions-sync push --cache-dir "cache" --destination-token $Env:TOKEN --destination-url $Env:TEST_INSTANCE_URL --repo-name-list "actions/setup-node,actions/checkout" --actions-admin-user actions-admin
|
||||||
|
|
||||||
|
# Testing Sync Multiple Existing Repos
|
||||||
|
echo "`n#########################`n### Testing Sync Multiple Existing Repos`n#########################"
|
||||||
|
bin/actions-sync sync --cache-dir "cache" --destination-token $Env:TOKEN --destination-url $Env:TEST_INSTANCE_URL --repo-name-list "actions/setup-node,actions/checkout" --actions-admin-user actions-admin
|
||||||
|
|
||||||
|
# Testing Sync New Single Repo
|
||||||
|
echo "`n#########################`n### Testing Sync New Single Repo`n#########################"
|
||||||
|
bin/actions-sync sync --cache-dir "cache" --destination-token $Env:TOKEN --destination-url $Env:TEST_INSTANCE_URL --repo-name-list "actions/actions-sync" --actions-admin-user actions-admin
|
||||||
@@ -123,6 +123,16 @@ function test_push() {
|
|||||||
push "pushing to authenticated user's account"
|
push "pushing to authenticated user's account"
|
||||||
assert_dest_sha "monalisa/new-repo" "heads/main" "e9009d51dd6da2c363d1d14779c53dd27fcb0c52" "updating monalisa/new-repo"
|
assert_dest_sha "monalisa/new-repo" "heads/main" "e9009d51dd6da2c363d1d14779c53dd27fcb0c52" "updating monalisa/new-repo"
|
||||||
|
|
||||||
|
# Push to GHAE with impersonation
|
||||||
|
setup_cache "org-already-exists/ghae-repo:heads/main:e9009d51dd6da2c363d1d14779c53dd27fcb0c52"
|
||||||
|
setup_dest "org-already-exists/ghae-repo:heads/main:a5984bb887dd2fcdc2892cd906d6f004844d1142"
|
||||||
|
push_impersonation "ghae-admin" "pushing to GHAE repo"
|
||||||
|
|
||||||
|
# Push to GHES with impersonation
|
||||||
|
setup_cache "org-already-exists/ghes-repo:heads/main:e9009d51dd6da2c363d1d14779c53dd27fcb0c52"
|
||||||
|
setup_dest "org-already-exists/ghes-repo:heads/main:a5984bb887dd2fcdc2892cd906d6f004844d1142"
|
||||||
|
push_impersonation "ghes-admin" "pushing to GHES repo"
|
||||||
|
|
||||||
echo "all push tests passed successfully"
|
echo "all push tests passed successfully"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,6 +323,17 @@ function push2args() {
|
|||||||
fail $3
|
fail $3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function push_impersonation() {
|
||||||
|
bin/actions-sync push \
|
||||||
|
--cache-dir "test/tmp/cache" \
|
||||||
|
--disable-push-git-auth \
|
||||||
|
--destination-token "token" \
|
||||||
|
--destination-url "http://localhost:$DEST_API_PORT" \
|
||||||
|
--actions-admin-user $1 \
|
||||||
|
&> $OUTPUT ||
|
||||||
|
fail "$2"
|
||||||
|
}
|
||||||
|
|
||||||
function sync() {
|
function sync() {
|
||||||
bin/actions-sync sync \
|
bin/actions-sync sync \
|
||||||
--cache-dir "test/tmp/cache" \
|
--cache-dir "test/tmp/cache" \
|
||||||
|
|||||||
+96
-17
@@ -17,9 +17,14 @@ import (
|
|||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const enterpriseAegisVersionHeaderValue = "GitHub AE"
|
||||||
|
const enterpriseAPIPath = "/api/v3"
|
||||||
|
const enterpriseVersionHeaderKey = "X-GitHub-Enterprise-Version"
|
||||||
|
const xOAuthScopesHeader = "X-OAuth-Scopes"
|
||||||
|
|
||||||
type PushOnlyFlags struct {
|
type PushOnlyFlags struct {
|
||||||
BaseURL, Token string
|
BaseURL, Token, ActionsAdminUser string
|
||||||
DisableGitAuth bool
|
DisableGitAuth bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type PushFlags struct {
|
type PushFlags struct {
|
||||||
@@ -34,6 +39,7 @@ func (f *PushFlags) Init(cmd *cobra.Command) {
|
|||||||
|
|
||||||
func (f *PushOnlyFlags) Init(cmd *cobra.Command) {
|
func (f *PushOnlyFlags) Init(cmd *cobra.Command) {
|
||||||
cmd.Flags().StringVar(&f.BaseURL, "destination-url", "", "URL of GHES instance")
|
cmd.Flags().StringVar(&f.BaseURL, "destination-url", "", "URL of GHES instance")
|
||||||
|
cmd.Flags().StringVar(&f.ActionsAdminUser, "actions-admin-user", "", "A user to impersonate for the push requests. To use the default name, pass 'actions-admin'. Note that the site_admin scope in the token is required for the impersonation to work.")
|
||||||
cmd.Flags().StringVar(&f.Token, "destination-token", "", "Token to access API on GHES instance")
|
cmd.Flags().StringVar(&f.Token, "destination-token", "", "Token to access API on GHES instance")
|
||||||
cmd.Flags().BoolVar(&f.DisableGitAuth, "disable-push-git-auth", false, "Disables git authentication whilst pushing")
|
cmd.Flags().BoolVar(&f.DisableGitAuth, "disable-push-git-auth", false, "Disables git authentication whilst pushing")
|
||||||
}
|
}
|
||||||
@@ -53,7 +59,64 @@ func (f *PushOnlyFlags) Validate() Validations {
|
|||||||
return validations
|
return validations
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetImpersonationToken(ctx context.Context, flags *PushFlags) (string, error) {
|
||||||
|
fmt.Printf("getting an impersonation token for `%s` ...\n", flags.ActionsAdminUser)
|
||||||
|
|
||||||
|
ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: flags.Token})
|
||||||
|
tc := oauth2.NewClient(ctx, ts)
|
||||||
|
ghClient, err := github.NewEnterpriseClient(flags.BaseURL, flags.BaseURL, tc)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(err, "error creating enterprise client")
|
||||||
|
}
|
||||||
|
|
||||||
|
rootRequest, err := ghClient.NewRequest("GET", enterpriseAPIPath, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(err, "error constructing request for GitHub Enterprise client.")
|
||||||
|
}
|
||||||
|
rootResponse, err := ghClient.Do(ctx, rootRequest, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(err, "error checking connectivity for GitHub Enterprise client.")
|
||||||
|
}
|
||||||
|
|
||||||
|
scopesHeader := rootResponse.Header.Get(xOAuthScopesHeader)
|
||||||
|
fmt.Printf("these are the scopes we have for the current token `%s` ...\n", scopesHeader)
|
||||||
|
|
||||||
|
if !strings.Contains(scopesHeader, "site_admin") {
|
||||||
|
return "", errors.New("the current token doesn't have the `site_admin` scope, the impersonation function requires the `site_admin` permission to be able to impersonate")
|
||||||
|
}
|
||||||
|
|
||||||
|
isAE := rootResponse.Header.Get(enterpriseVersionHeaderKey) == enterpriseAegisVersionHeaderValue
|
||||||
|
minimumRepositoryScope := "public_repo"
|
||||||
|
if isAE {
|
||||||
|
// the default repository scope for non-ae instances is 'public_repo'
|
||||||
|
// while it is `repo` for ae.
|
||||||
|
minimumRepositoryScope = "repo"
|
||||||
|
fmt.Printf("running against GitHub AE, changing the repository scope to '%s' ...\n", minimumRepositoryScope)
|
||||||
|
}
|
||||||
|
|
||||||
|
impersonationToken, _, err := ghClient.Admin.CreateUserImpersonation(ctx, flags.ActionsAdminUser, &github.ImpersonateUserOptions{Scopes: []string{minimumRepositoryScope, "workflow"}})
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(err, "failed to impersonate Actions admin user.")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("got the impersonation token for `%s` ...\n", flags.ActionsAdminUser)
|
||||||
|
|
||||||
|
return impersonationToken.GetToken(), nil
|
||||||
|
}
|
||||||
|
|
||||||
func Push(ctx context.Context, flags *PushFlags) error {
|
func Push(ctx context.Context, flags *PushFlags) error {
|
||||||
|
if flags.ActionsAdminUser != "" {
|
||||||
|
var token, err = GetImpersonationToken(ctx, flags)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "error obtaining the impersonation token")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override the initial token with the one that we got in the exchange
|
||||||
|
flags.Token = token
|
||||||
|
} else {
|
||||||
|
fmt.Print("not using impersonation for the requests \n")
|
||||||
|
}
|
||||||
|
|
||||||
ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: flags.Token})
|
ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: flags.Token})
|
||||||
tc := oauth2.NewClient(ctx, ts)
|
tc := oauth2.NewClient(ctx, ts)
|
||||||
ghClient, err := github.NewEnterpriseClient(flags.BaseURL, flags.BaseURL, tc)
|
ghClient, err := github.NewEnterpriseClient(flags.BaseURL, flags.BaseURL, tc)
|
||||||
@@ -116,21 +179,16 @@ func PushWithGitImpl(ctx context.Context, flags *PushFlags, repoName string, ghC
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getOrCreateGitHubRepo(ctx context.Context, client *github.Client, repoName, ownerName string) (*github.Repository, error) {
|
func getOrCreateGitHubRepo(ctx context.Context, client *github.Client, repoName, ownerName string) (*github.Repository, error) {
|
||||||
repo := &github.Repository{
|
// retrieve user associated to authentication credentials provided
|
||||||
Name: github.String(repoName),
|
currentUser, userResponse, err := client.Users.Get(ctx, "")
|
||||||
HasIssues: github.Bool(false),
|
|
||||||
HasWiki: github.Bool(false),
|
|
||||||
HasPages: github.Bool(false),
|
|
||||||
HasProjects: github.Bool(false),
|
|
||||||
}
|
|
||||||
|
|
||||||
currentUser, _, err := client.Users.Get(ctx, "")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "error retrieving authenticated user")
|
return nil, errors.Wrap(err, "error retrieving authenticated user")
|
||||||
}
|
}
|
||||||
if currentUser == nil || currentUser.Login == nil {
|
if currentUser == nil || currentUser.Login == nil {
|
||||||
return nil, errors.New("error retrieving authenticated user's login name")
|
return nil, errors.New("error retrieving authenticated user's login name")
|
||||||
}
|
}
|
||||||
|
// checking if we talk to GHAE
|
||||||
|
isAE := userResponse.Header.Get(enterpriseVersionHeaderKey) == enterpriseAegisVersionHeaderValue
|
||||||
|
|
||||||
// check if the owner refers to the authenticated user or an organization.
|
// check if the owner refers to the authenticated user or an organization.
|
||||||
var createRepoOrgName string
|
var createRepoOrgName string
|
||||||
@@ -146,15 +204,36 @@ func getOrCreateGitHubRepo(ctx context.Context, client *github.Client, repoName,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ghRepo, resp, err := client.Repositories.Create(ctx, createRepoOrgName, repo)
|
// check if repository already exists
|
||||||
|
ghRepo, resp, err := client.Repositories.Get(ctx, ownerName, repoName)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
fmt.Printf("Created repo `%s/%s`\n", ownerName, repoName)
|
fmt.Printf("Existing repo `%s/%s`\n", ownerName, repoName)
|
||||||
} else if resp != nil && resp.StatusCode == 422 {
|
} else if resp != nil && resp.StatusCode == 404 {
|
||||||
ghRepo, _, err = client.Repositories.Get(ctx, ownerName, repoName)
|
// repo not existing yet - try to create
|
||||||
}
|
visibility := github.String("public")
|
||||||
if err != nil {
|
if isAE {
|
||||||
|
visibility = github.String("internal")
|
||||||
|
}
|
||||||
|
repo := &github.Repository{
|
||||||
|
Name: github.String(repoName),
|
||||||
|
HasIssues: github.Bool(false),
|
||||||
|
HasWiki: github.Bool(false),
|
||||||
|
HasPages: github.Bool(false),
|
||||||
|
HasProjects: github.Bool(false),
|
||||||
|
Visibility: visibility,
|
||||||
|
}
|
||||||
|
|
||||||
|
ghRepo, _, err = client.Repositories.Create(ctx, createRepoOrgName, repo)
|
||||||
|
if err == nil {
|
||||||
|
fmt.Printf("Created repo `%s/%s`\n", ownerName, repoName)
|
||||||
|
} else {
|
||||||
|
return nil, errors.Wrapf(err, "error creating repository %s/%s", ownerName, repoName)
|
||||||
|
}
|
||||||
|
} else if err != nil {
|
||||||
return nil, errors.Wrapf(err, "error creating repository %s/%s", ownerName, repoName)
|
return nil, errors.Wrapf(err, "error creating repository %s/%s", ownerName, repoName)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ghRepo == nil {
|
if ghRepo == nil {
|
||||||
return nil, errors.New("error repository is nil")
|
return nil, errors.New("error repository is nil")
|
||||||
}
|
}
|
||||||
|
|||||||
+59
-5
@@ -8,15 +8,20 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path"
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/google/go-github/v43/github"
|
"github.com/google/go-github/v43/github"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
var authenticatedLogin string = "monalisa"
|
var authenticatedLogin string = "monalisa"
|
||||||
var existingOrg string = "org-already-exists"
|
|
||||||
var existingRepo string = "repo-already-exists"
|
|
||||||
|
|
||||||
|
const existingOrg string = "org-already-exists"
|
||||||
|
const existingRepo string = "repo-already-exists"
|
||||||
|
const ghaeRepo string = "ghae-repo"
|
||||||
|
const xOAuthScopesHeader = "X-OAuth-Scopes"
|
||||||
|
|
||||||
|
//nolint:gocyclo
|
||||||
func main() {
|
func main() {
|
||||||
var port, gitDaemonURL string
|
var port, gitDaemonURL string
|
||||||
flag.StringVar(&port, "p", "", "")
|
flag.StringVar(&port, "p", "", "")
|
||||||
@@ -26,7 +31,16 @@ func main() {
|
|||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
r.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {})
|
r.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {})
|
||||||
|
|
||||||
|
r.HandleFunc("/api/v3", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("x-github-enterprise-version", "GitHub AE")
|
||||||
|
w.Header().Set(xOAuthScopesHeader, "site_admin")
|
||||||
|
})
|
||||||
|
|
||||||
r.HandleFunc("/api/v3/user", func(w http.ResponseWriter, r *http.Request) {
|
r.HandleFunc("/api/v3/user", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
token := r.Header.Get("Authorization")
|
||||||
|
if strings.Contains(token, "ghaetoken") {
|
||||||
|
w.Header().Set("x-github-enterprise-version", "GitHub AE")
|
||||||
|
}
|
||||||
currentUser := github.User{Login: &authenticatedLogin}
|
currentUser := github.User{Login: &authenticatedLogin}
|
||||||
b, _ := json.Marshal(currentUser)
|
b, _ := json.Marshal(currentUser)
|
||||||
_, err := w.Write(b)
|
_, err := w.Write(b)
|
||||||
@@ -35,6 +49,27 @@ func main() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
r.HandleFunc("/api/v3/admin/users/ghes-admin/authorizations", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
token := "token"
|
||||||
|
auth := github.Authorization{Token: &token}
|
||||||
|
b, _ := json.Marshal(auth)
|
||||||
|
_, err := w.Write(b)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}).Methods("POST")
|
||||||
|
|
||||||
|
r.HandleFunc("/api/v3/admin/users/ghae-admin/authorizations", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("x-github-enterprise-version", "GitHub AE")
|
||||||
|
token := "ghaetoken"
|
||||||
|
auth := github.Authorization{Token: &token}
|
||||||
|
b, _ := json.Marshal(auth)
|
||||||
|
_, err := w.Write(b)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}).Methods("POST")
|
||||||
|
|
||||||
r.HandleFunc("/api/v3/admin/organizations", func(w http.ResponseWriter, r *http.Request) {
|
r.HandleFunc("/api/v3/admin/organizations", func(w http.ResponseWriter, r *http.Request) {
|
||||||
b, err := ioutil.ReadAll(r.Body)
|
b, err := ioutil.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -99,16 +134,35 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
var repoReq struct {
|
var repoReq struct {
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
|
Visibility string `json:"visibility,omitempty"`
|
||||||
}
|
}
|
||||||
err = json.Unmarshal(b, &repoReq)
|
err = json.Unmarshal(b, &repoReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if repoReq.Name == "repo-already-exists" {
|
var errString string = ""
|
||||||
|
// check visibility requirements
|
||||||
|
if repoReq.Name == ghaeRepo {
|
||||||
|
if repoReq.Visibility != "internal" {
|
||||||
|
errString = fmt.Sprintf("Provided repo visibility %s for GHAE must be internal", repoReq.Visibility)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if repoReq.Visibility != "public" {
|
||||||
|
errString = fmt.Sprintf("Provided repo visibility %s for GHES must be public", repoReq.Visibility)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if we are testing existing Repo
|
||||||
|
if repoReq.Name == existingRepo {
|
||||||
|
errString = fmt.Sprintf("Repo %s already exists", html.EscapeString(repoReq.Name))
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there is an error throw it back
|
||||||
|
if errString != "" {
|
||||||
w.WriteHeader(http.StatusUnprocessableEntity)
|
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||||
_, err := w.Write([]byte(fmt.Sprintf("Repo %s already exists", html.EscapeString(repoReq.Name))))
|
_, err := w.Write([]byte(errString))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user