예제 #1
0
        public static async Task <string> GetOrCreateResourceGroup(GithubRepo repo)
        {
            using (var client = await CreateClient(repo))
            {
                var path = $"resourceGroups/{repo.AzureData.ResourceGroup}?api-version=2018-02-01";
                var resp = await client.GetAsync(path);

                if (resp.StatusCode == HttpStatusCode.OK)
                {
                    return(repo.AzureData.ResourceGroup);
                }
                if (resp.StatusCode == HttpStatusCode.NotFound)
                {
                    var input = new
                    {
                        location   = repo.AzureData.Location,
                        properties = new { },
                        name       = repo.AzureData.ResourceGroup,
                    };
                    resp = await SendMessage(client, path, HttpMethod.Put, input);

                    var data = await resp.Content.ReadAsStringAsync();

                    resp.EnsureSuccessStatusCode();
                    return(repo.AzureData.ResourceGroup);;
                }
                var error = await resp.Content.ReadAsStringAsync();

                resp.EnsureSuccessStatusCode();
            }
            return(repo.AzureData.ResourceGroup);
        }
예제 #2
0
        public static async Task <string> CreateAppService(GithubRepo repo, Build build)
        {
            var name = string.IsNullOrWhiteSpace(build.AzureAppId) ? $"{repo.Id}-Pull{build.PullRequestId}-{Guid.NewGuid().ToString().Substring(0, 8)}" : build.AzureAppId.Replace("_", "-");

            using (var client = await CreateClient(repo))
            {
                var path  = $"resourceGroups/{repo.AzureData.ResourceGroup}/providers/Microsoft.Web/serverfarms/{name}?api-version=2016-09-01";
                var input = new
                {
                    name       = name,
                    location   = repo.AzureData.Location,
                    properties = new
                    {
                        name           = name,
                        perSiteScaling = false
                    },
                    sku = new
                    {
                        name     = "F1",
                        tier     = "Free",
                        capacity = 1
                    }
                };

                var resp = await SendMessage(client, path, HttpMethod.Put, input, new Dictionary <string, string> {
                    ["CommandName"] = "appservice plan create"
                });

                var data = await resp.Content.ReadAsStringAsync();

                resp.EnsureSuccessStatusCode();
                build.AzureAppId = name;
                return(name);
            }
        }
예제 #3
0
        static async Task <IActionResult> CleanupOldApp(HttpRequest req, dynamic data)
        {
            var        repData = data.repository;
            var        id      = Database.CleanseName(repData.full_name.ToString());
            GithubRepo repo;

            try{
                repo = await Database.GetRepo(id);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                repo = new GithubRepo
                {
                    Id        = id,
                    Owner     = repData.owner.login,
                    RepoName  = repData.Name,
                    CloneUrl  = repData.clone_url,
                    IsPrivate = repData.@private,
                    AzureData =
                    {
                        Location = "southcentralus",
                    }
                };
                await Database.CreateRepo(repo);
            }

            var    pullRequest = data.pull_request;
            string statusUrl   = pullRequest.statuses_url;

            var isAuthenticated = await AzureApi.Authenticate(repo);

            if (!isAuthenticated)
            {
                var paring = await Database.GetOrCreatePairingRequestByRepoId(id);

                var orgUrl = req.Host.Value;
                var url    = $"{req.Scheme}://{orgUrl}/api/RegisterRepo?id={paring.Id}";
                await GithubApi.PostStatus(repo, statusUrl, false, url, $"You need to log into Azure before auto deploy will work. {url}");

                return(new BadRequestObjectResult($"Please go to {url} to register the app with azure."));
            }

            if (repo.Builds == null)
            {
                repo.Builds = new List <Build>();
            }
            var number = (int)data.number;
            var build  = repo.Builds?.FirstOrDefault(x => x.PullRequestId == number);

            if (!string.IsNullOrWhiteSpace(build?.AzureAppId))
            {
                await AzureApi.DeleteWebApp(repo, build);

                await AzureApi.DeleteAppService(repo, build);
            }
            return(new OkResult());
        }
예제 #4
0
        public static async Task <Document> CreateRepo(GithubRepo repo)
        {
            var collectionLink = UriFactory.CreateDocumentCollectionUri(databaseId, collectionId);

            using (var client = new DocumentClient(new Uri(endpointUrl), authorizationKey))
            {
                return(await client.CreateDocumentAsync(collectionLink, repo));
            }
        }
예제 #5
0
        public static async Task <GithubRepo> Save(GithubRepo repo)
        {
            var collectionLink = UriFactory.CreateDocumentCollectionUri(databaseId, collectionId);

            using (var client = new DocumentClient(new Uri(endpointUrl), authorizationKey))
            {
                var resp = await client.UpsertDocumentAsync(collectionLink, repo);

                return(repo);
            }
        }
예제 #6
0
        public static async Task <bool> DeleteWebApp(GithubRepo repo, Build build)
        {
            using (var client = await CreateClient(repo))
            {
                var path = $"resourceGroups/{repo.AzureData.ResourceGroup}/providers/Microsoft.Web/sites/{build.AzureAppId}?api-version=2016-08-01";
                var resp = await SendMessage(client, path, HttpMethod.Delete, null);

                var data = await resp.Content.ReadAsStringAsync();

                resp.EnsureSuccessStatusCode();
                build.IsActive = false;
                return(true);
            }
        }
예제 #7
0
        static async Task <string> FormatRepoCloneUrl(GithubRepo repo, Build build)
        {
            var baseUrl = string.IsNullOrWhiteSpace(build.GitUrl) ? repo.CloneUrl : build.GitUrl;
            var url     = $"{baseUrl}#{build.CommitHash}";

            if (repo.IsPrivate)
            {
                await GithubApi.Authenticate(repo);

                var uri = new Uri(url);
                url = $"{uri.Host}://{repo.GithubAccount.Token}@{uri.Host}/{uri.PathAndQuery}";
            }
            return(url);
        }
예제 #8
0
        public static async Task <bool> Authenticate(GithubRepo repo)
        {
            if (repo.GithubAccount.IsValid())
            {
                return(true);
            }
            try
            {
                var postData = new Dictionary <string, string>
                {
                    ["grant_type"]    = "refresh_token",
                    ["refresh_token"] = repo.GithubAccount.RefreshToken,
                    ["client_id"]     = clientId,
                    ["client_secret"] = clientSecret,
                };

                var message = new HttpRequestMessage(HttpMethod.Post, TokenUrl)
                {
                    Content = new FormUrlEncodedContent(postData),
                    Headers =
                    {
                        { "Accept", "application/json" }
                    }
                };
                using (var client = await CreateClient(repo, false))
                {
                    var reply = await client.SendAsync(message);

                    var resp = await reply.Content.ReadAsStringAsync();

                    var result = JsonConvert.DeserializeObject <OauthResponse>(resp);
                    if (!string.IsNullOrEmpty(result.RefreshToken))
                    {
                        repo.GithubAccount.RefreshToken = result.RefreshToken;
                    }
                    repo.GithubAccount.TokenType = result.TokenType;
                    repo.GithubAccount.Token     = result.AccessToken;
                    repo.GithubAccount.ExpiresIn = result.ExpiresIn;
                    repo.GithubAccount.Created   = DateTime.UtcNow;
                }
                await Database.Save(repo);

                return(true);
            }
            catch (Exception)
            {
                return(false);
            }
        }
예제 #9
0
        // static HttpClient CreateClient(GithubRepo repo) => new HttpClient
        // {
        //     BaseAddress = new Uri($"https://management.azure.com/subscriptions/{repo.AzureData.Subscription}/"),
        //     DefaultRequestHeaders = {
        //         Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(repo.AzureAccount.TokenType, repo.AzureAccount.Token)
        //     }
        // };

        static async Task <HttpClient> CreateClient(GithubRepo repo, bool authenticate = true)
        {
            if (authenticate)
            {
                await Authenticate(repo);
            }
            return(new HttpClient
            {
                BaseAddress = new Uri($"https://management.azure.com/subscriptions/{repo.AzureData.Subscription}/"),
                DefaultRequestHeaders =
                {
                    Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(repo.AzureAccount.TokenType, repo.AzureAccount.Token)
                }
            });
        }
예제 #10
0
        public static async Task <bool> DeleteAppService(GithubRepo repo, Build build)
        {
            var name = string.IsNullOrWhiteSpace(build.AzureAppId) ? $"{repo.Id}-Pull{build.PullRequestId}-{Guid.NewGuid().ToString().Substring(0, 8)}" : build.AzureAppId.Replace("_", "-");

            using (var client = await CreateClient(repo))
            {
                var path = $"resourceGroups/{repo.AzureData.ResourceGroup}/providers/Microsoft.Web/serverfarms/{name}?api-version=2016-09-01";
                var resp = await SendMessage(client, path, HttpMethod.Delete, null);

                var data = await resp.Content.ReadAsStringAsync();

                resp.EnsureSuccessStatusCode();
                build.AzureAppId = name;
                return(true);
            }
        }
예제 #11
0
        public static async Task <bool> Authenticate(GithubRepo repo, string code, string redirect)
        {
            var authUrl  = $"https://login.microsoftonline.com/{azureTennant}/oauth2/authorize";
            var postData = new Dictionary <string, string>
            {
                ["grant_type"]    = "authorization_code",
                ["code"]          = code,
                ["client_id"]     = clientId,
                ["client_secret"] = clientSecret,
                ["redirect_uri"]  = redirect
            };
            var message = new HttpRequestMessage(HttpMethod.Post, TokenUrl)
            {
                Content = new FormUrlEncodedContent(postData),
                Headers =
                {
                    { "Accept", "application/json" }
                }
            };

            using (var client = new HttpClient())
            {
                var reply = await client.SendAsync(message);

                var resp = await reply.Content.ReadAsStringAsync();

                var result = JsonConvert.DeserializeObject <OauthResponse>(resp);
                if (!string.IsNullOrEmpty(result?.Error))
                {
                    throw new Exception($"{result.Error} : {result.ErrorDescription}");
                }
                reply.EnsureSuccessStatusCode();

                repo.AzureAccount = new Account()
                {
                    ExpiresIn    = result.ExpiresIn,
                    Created      = DateTime.UtcNow,
                    RefreshToken = result.RefreshToken,
                    TokenType    = result.TokenType,
                    Token        = result.AccessToken,
                    IdToken      = result.Id
                };
                await Database.Save(repo);
            }
            return(true);
        }
예제 #12
0
        static async Task <HttpClient> CreateClient(GithubRepo repo, bool authenticate = true)
        {
            if (authenticate)
            {
                await Authenticate(repo);
            }
            var client = new HttpClient
            {
                BaseAddress           = new Uri($"https://api.github.com"),
                DefaultRequestHeaders =
                {
                    Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(repo.GithubAccount.TokenType, repo.GithubAccount.Token)
                }
            };

            client.DefaultRequestHeaders.Add("User-Agent", "AzureDeploy");
            return(client);
        }
예제 #13
0
        public static async Task <bool> PostStatus(GithubRepo repo, string statuUrl, bool success, string url, string description)
        {
            using (var client = await CreateClient(repo))
            {
                var resp = await SendMessage(client, statuUrl, HttpMethod.Post, new
                {
                    state       = success ? "success" : "error",
                    description = description,
                    target_url  = url,
                    context     = "continuous-integration/AzureDeploy",
                });

                var data = await resp.Content.ReadAsStringAsync();

                resp.EnsureSuccessStatusCode();
                return(true);
            }
        }
예제 #14
0
        static async Task <IActionResult> CreateNewApp(HttpRequest req, dynamic data)
        {
            var repData = data.repository;
            var id      = Database.CleanseName(repData.full_name.ToString());
            var repo    = new GithubRepo
            {
                Id        = id,
                Owner     = repData.owner.login,
                RepoName  = repData.Name,
                IsPrivate = repData.@private,
            };

            try{
                var doc = await Database.CreateRepo(repo);
            }
            catch (Exception)
            {
            }
            return(await GetRegisterUrl(req, id));
        }
예제 #15
0
        public static async Task <string> CreateWebApp(GithubRepo repo, Build build)
        {
            using (var client = await CreateClient(repo))
            {
                var path = $"resourceGroups/{repo.AzureData.ResourceGroup}/providers/Microsoft.Web/sites/{build.AzureAppId}?api-version=2016-08-01";
                var resp = await SendMessage(client, path, HttpMethod.Put, new
                {
                    location   = repo.AzureData.Location,
                    properties = new
                    {
                        name = build.AzureAppId,
                    }
                });

                build.IsActive = true;
                var data = await resp.Content.ReadAsStringAsync();

                resp.EnsureSuccessStatusCode();
                return(build.AzureAppId);
            }
        }
예제 #16
0
        public static async Task <bool> PublishPullRequst(GithubRepo repo, Build build)
        {
            using (var client = new HttpClient())
            {
                client.BaseAddress = new Uri($"https://{build.AzureAppId}.scm.azurewebsites.net/");
                var credentials = await GetAzureDeployCredentials(repo, build);

                var key = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{credentials.username}:{credentials.password}"));
                client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", key);

                //Switch branches
                var resp = await SendMessage(client, "settings", HttpMethod.Post, new
                {
                    key   = "branch",
                    value = build.Branch,
                });

                var data = await resp.Content.ReadAsStringAsync();

                resp.EnsureSuccessStatusCode();

                var url      = $"https://{build.AzureAppId}.scm.azurewebsites.net/deploy";
                var cloneUrl = await FormatRepoCloneUrl(repo, build);

                var payload = new
                {
                    format = "basic",
                    url    = cloneUrl
                };
                resp = await SendMessage(client, url, HttpMethod.Post, payload);

                data = await resp.Content.ReadAsStringAsync();

                return(resp.IsSuccessStatusCode);
            }
        }
예제 #17
0
        static async Task <IActionResult> BuildPullRequest(HttpRequest req, dynamic data)
        {
            var        repData = data.repository;
            var        id      = Database.CleanseName(repData.full_name.ToString());
            GithubRepo repo;

            try{
                repo = await Database.GetRepo(id);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                repo = new GithubRepo
                {
                    Id        = id,
                    Owner     = repData.owner.login,
                    RepoName  = repData.Name,
                    CloneUrl  = repData.clone_url,
                    IsPrivate = repData.@private,
                    AzureData =
                    {
                        Location = "southcentralus",
                    }
                };
                await Database.CreateRepo(repo);
            }

            var    pullRequest           = data.pull_request;
            string statusUrl             = pullRequest.statuses_url;
            var    isGithubAuthenticated = await GithubApi.Authenticate(repo);

            if (!isGithubAuthenticated)
            {
                var paring = await Database.GetOrCreatePairingRequestByRepoId(id);

                var orgUrl = req.Host.Value;
                var url    = $"{req.Scheme}://{orgUrl}/api/RegisterRepo?id={paring.Id}";
                return(new BadRequestObjectResult($"Please go to {url} to register the app with Github."));
            }

            var isAuthenticated = await AzureApi.Authenticate(repo);

            if (!isAuthenticated)
            {
                var paring = await Database.GetOrCreatePairingRequestByRepoId(id);

                var orgUrl = req.Host.Value;
                var url    = $"{req.Scheme}://{orgUrl}/api/RegisterRepo?id={paring.Id}";
                await GithubApi.PostStatus(repo, statusUrl, false, url, $"You need to log into Azure before auto deploy will work. {url}");

                return(new BadRequestObjectResult($"Please go to {url} to register the app with azure."));
            }

            if (repo.Builds == null)
            {
                repo.Builds = new List <Build>();
            }
            var number = (int)data.number;
            var build  = repo.Builds?.FirstOrDefault(x => x.PullRequestId == number);

            if (build == null)
            {
                build = new Build {
                    PullRequestId = number,
                };
                repo.Builds.Add(build);
            }
            build.CommitHash = pullRequest.head.sha;
            build.Branch     = pullRequest.head.@ref;
            build.StatusUrl  = statusUrl;
            string buildUrl = pullRequest.head.repo.clone_url;

            if (repo.CloneUrl != buildUrl)
            {
                build.GitUrl = buildUrl;
            }
            //Make sure we have a resource group!
            repo.AzureData.ResourceGroup = await AzureApi.GetOrCreateResourceGroup(repo);

            bool shouldSave = string.IsNullOrWhiteSpace(build.AzureAppId);

            build.AzureAppId = await AzureApi.CreateAppService(repo, build);

            if (shouldSave)
            {
                await Database.Save(repo);
            }
            await AzureApi.CreateWebApp(repo, build);

            build.DeployedUrl = $"http://{build.AzureAppId}.azurewebsites.net";
            var success = await AzureApi.PublishPullRequst(repo, build);

            await GithubApi.PostStatus(repo, statusUrl, success, build.DeployedUrl, success?$"Deployment was successful! {build.DeployedUrl}" : "There was an error deploying");

            await Database.Save(repo);

            return(new OkResult());
        }
예제 #18
0
        public static async Task <(string username, string password)> GetAzureDeployCredentials(GithubRepo repo, Build build)
        {
            using (var client = await CreateClient(repo))
            {
                var resp = await client.PostAsync($"resourceGroups/{repo.AzureData.ResourceGroup}/providers/Microsoft.Web/sites/{build.AzureAppId}/publishxml?api-version=2015-08-01", null);

                var stream = await resp.Content.ReadAsStreamAsync();

                var xml      = XElement.Load(stream);
                var element  = xml.Elements("publishProfile").Where(el => (string)el.Attribute("publishMethod") == "MSDeploy").FirstOrDefault();
                var username = (string)element.Attribute("userName");
                var password = (string)element.Attribute("userPWD");

                return(username, password);
            }
        }