import { Injectable , OnDestroy } from '@angular/core' ; import { Subject } from 'rxjs/Subject' ; import { Constants , DeploymentCenterConstants , RuntimeStacks , JavaContainers } from 'app/shared/models/constants' ; import { CacheService } from 'app/shared/services/cache.service' ; import { Guid } from 'app/shared/Utilities/Guid' ; import { Observable } from 'rxjs/Observable' ; import { FileContent , WorkflowInformation , GitHubActionWorkflowRequestContent , GitHubCommit } from '../../Models/github' ; import { BuildSettings , SourceSettings } from './deployment-center-setup-models' ; import { Response } from '@angular/http' ; @ Injectable ( ) export class GithubService implements OnDestroy { private _ngUnsubscribe$ = new Subject ( ) ; constructor ( private _cacheService ) { } ngOnDestroy ( ) { this . _ngUnsubscribe$ . next ( ) ; } fetchOrgs ( gitHubToken ) { return this . _cacheService . post ( Constants . serviceHost + 'api/github/passthrough?orgs=' , true , null , { url : `${ DeploymentCenterConstants . githubApiUrl }/user/orgs` , gitHubToken , } ) ; } fetchUser ( gitHubToken ) { return this . _cacheService . post ( Constants . serviceHost + 'api/github/passthrough?user=' , true , null , { url : `${ DeploymentCenterConstants . githubApiUrl }/user` , gitHubToken , } ) ; } fetchUserRepos ( gitHubToken , org , page ? , ownerAccessRequired = true ) { let url ; if ( ownerAccessRequired ) { url = page ? `${ DeploymentCenterConstants . githubApiUrl }/user/repos?type=owner&page=${ page }` : `${ DeploymentCenterConstants . githubApiUrl }/user/repos?type=owner` ; } else { url = page ? `${ DeploymentCenterConstants . githubApiUrl }/user/repos?page=${ page }` : `${ DeploymentCenterConstants . githubApiUrl }/user/repos` ; } return this . _cacheService . post ( Constants . serviceHost + `api/github/passthrough?repo=${ org }&t=${ Guid . newTinyGuid ( ) }` , true , null , { url , gitHubToken , } ) ; } fetchOrgRepos ( gitHubToken , org , page ? ) { const url = page ? `${ org }/repos?per_page=100&page=${ page }` : `${ org }/repos?per_page=100` ; return this . _cacheService . post ( Constants . serviceHost + `api/github/passthrough?repo=${ org }&t=${ Guid . newTinyGuid ( ) }` , true , null , { url , gitHubToken , } ) ; } fetchRepo ( gitHubToken , repoUrl , repoName ) { return this . _cacheService . post ( Constants . serviceHost + `api/github/passthrough?branch=${ repoUrl }&t=${ Guid . newTinyGuid ( ) }` , true , null , { url : `${ DeploymentCenterConstants . githubApiUrl }/repos/${ repoName }` , gitHubToken , } ) ; } fetchBranches ( gitHubToken , repoUrl , repoName , page ? ) { const url = page ? `${ DeploymentCenterConstants . githubApiUrl }/repos/${ repoName }/branches?per_page=100&page=${ page }` : `${ DeploymentCenterConstants . githubApiUrl }/repos/${ repoName }/branches?per_page=100` ; return this . _cacheService . post ( Constants . serviceHost + `api/github/passthrough?branch=${ repoUrl }&t=${ Guid . newTinyGuid ( ) }` , true , null , { url , gitHubToken , } ) ; } fetchBranch ( gitHubToken , repoUrl , repoName , branchName ) { return this . _cacheService . post ( Constants . serviceHost + `api/github/passthrough?branch=${ repoUrl }&t=${ Guid . newTinyGuid ( ) }` , true , null , { url : `${ DeploymentCenterConstants . githubApiUrl }/repos/${ repoName }/branches/${ branchName }` , gitHubToken , } ) ; } fetchAllWorkflowConfigurations ( gitHubToken , repoUrl , repoName , branchName ) { return this . _cacheService . post ( Constants . serviceHost + `api/github/passthrough?branch=${ repoUrl }/contents/.github/workflows&t=${ Guid . newTinyGuid ( ) }` , true , null , { url : `${ DeploymentCenterConstants . githubApiUrl }/repos/${ repoName }/contents/.github/workflows?ref=${ branchName }` , gitHubToken , } ) . map ( r => r . json ( ) ) . catch ( e => Observable . of ( null ) ) ; } fetchWorkflowConfiguration ( gitHubToken , repoUrl , repoName , branchName , workflowYmlPath ) { return this . _cacheService . post ( Constants . serviceHost + `api/github/passthrough?branch=${ repoUrl }/contents/${ workflowYmlPath }&t=${ Guid . newTinyGuid ( ) }` , true , null , { url : `${ DeploymentCenterConstants . githubApiUrl }/repos/${ repoName }/contents/${ workflowYmlPath }?ref=${ branchName }` , gitHubToken , } ) . map ( r => r . json ( ) ) . catch ( e => Observable . of ( null ) ) ; } createOrUpdateActionWorkflow ( authToken , gitHubToken , content , replacementPublishUrl ? ) { return this . _cacheService . put ( Constants . serviceHost + `api/github/actionWorkflow` , null , { authToken , gitHubToken , content , replacementPublishUrl , } ) ; } deleteActionWorkflow ( gitHubToken , deleteCommit ) { return this . _cacheService . post ( Constants . serviceHost + `api/github/deleteActionWorkflow` , true , null , { gitHubToken , deleteCommit , } ) ; } getWorkflowInformation ( buildSettings , sourceSettings , isLinuxApp , secretNameGuid , siteName , slotName ? ) { const branch = sourceSettings . branch || 'master' ; const fileName = this . getWorkflowFileName ( branch , siteName , slotName ) ; const secretName = `AzureAppService_PublishProfile_${ secretNameGuid }` ; let content = '' ; const runtimeStackVersion = this . _getRuntimeVersion ( isLinuxApp , buildSettings ) ; switch ( buildSettings . runtimeStack ) { case RuntimeStacks . node : content = this . _getNodeGithubActionWorkflowDefinition ( siteName , slotName , branch , isLinuxApp , secretName , runtimeStackVersion ) ; break ; case RuntimeStacks . python : content = isLinuxApp ? this . _getPythonGithubActionWorkflowDefinitionForLinux ( siteName , slotName , branch , secretName , runtimeStackVersion ) : this . _getPythonGithubActionWorkflowDefinitionForWindows ( siteName , slotName , branch , secretName , runtimeStackVersion ) ; break ; case RuntimeStacks . dotnetcore : content = this . _getDotnetCoreGithubActionWorkflowDefinition ( siteName , slotName , branch , isLinuxApp , secretName , runtimeStackVersion ) ; break ; case RuntimeStacks . java8 : case RuntimeStacks . java11 : if ( this . _isJavaWarBuild ( buildSettings ) ) { content = this . _getJavaWarGithubActionWorkflowDefinition ( siteName , slotName , branch , isLinuxApp , secretName , runtimeStackVersion ) ; } else { content = this . _getJavaJarGithubActionWorkflowDefinition ( siteName , slotName , branch , isLinuxApp , secretName , runtimeStackVersion ) ; } break ; case RuntimeStacks . aspnet : content = buildSettings . runtimeStackVersion . toLocaleLowerCase ( ) === 'dotnetcore|5.0' || buildSettings . runtimeStackVersion . toLocaleLowerCase ( ) === 'v5.0' ? this . _getDotnetCoreGithubActionWorkflowDefinition ( siteName , slotName , branch , isLinuxApp , secretName , runtimeStackVersion ) : this . _getAspNetGithubActionWorkflowDefinition ( siteName , slotName , branch , secretName , runtimeStackVersion ) ; break ; default : throw Error ( `Incorrect stack value '${ buildSettings . runtimeStack }' provided.` ) ; } return { fileName , secretName , content : content , } ; } getWorkflowFileName ( branch , siteName , slotName ? ) { const normalizedBranchName = branch . split ( '/' ) . join ( '-' ) ; return slotName ? `${ normalizedBranchName }_${ siteName }(${ slotName }).yml` : `${ normalizedBranchName }_${ siteName }.yml` ; } private _getRuntimeVersion ( isLinuxApp , buildSettings ) { if ( buildSettings . runtimeStackRecommendedVersion ) { return buildSettings . runtimeStackRecommendedVersion ; } else { return isLinuxApp ? buildSettings . runtimeStackVersion . split ( '|' ) [ 1 ] : buildSettings . runtimeStackVersion ; } } private _isJavaWarBuild ( buildSettings ) { return buildSettings . runtimeStackVersion . toLocaleLowerCase ( ) . indexOf ( JavaContainers . Tomcat ) > - 1 ; } private _getNodeGithubActionWorkflowDefinition ( siteName , slotName , branch , isLinuxApp , secretName , runtimeStackVersion ) { const webAppName = slotName ? `${ siteName }(${ slotName })` : siteName ; const slot = slotName || 'production' ; return `# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
# More GitHub Actions for Azure: https://github.com/Azure/actions

name: Build and deploy Node.js app to Azure Web App - ${ webAppName }

on:
  push:
    branches:
      - ${ branch }

jobs:
  build-and-deploy:
    runs-on: ${ isLinuxApp ? 'ubuntu-latest' : 'windows-latest' }

    steps:
    - uses: actions/checkout@v2

    - name: Set up Node.js version
      uses: actions/setup-node@v1
      with:
        node-version: '${ runtimeStackVersion }'

    - name: npm install, build, and test
      run: |
        npm install
        npm run build --if-present
        npm run test --if-present

    - name: 'Deploy to Azure Web App'
      uses: azure/webapps-deploy@v2
      with:
        app-name: '${ siteName }'
        slot-name: '${ slot }'
        publish-profile: \${{ secrets.${ secretName } }}
        package: .` ; } private _getPythonGithubActionWorkflowDefinitionForWindows ( siteName , slotName , branch , secretName , runtimeStackVersion ) { const webAppName = slotName ? `${ siteName }(${ slotName })` : siteName ; const slot = slotName || 'production' ; return `# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
# More GitHub Actions for Azure: https://github.com/Azure/actions

name: Build and deploy Python app to Azure Web App - ${ webAppName }

on:
  push:
    branches:
      - ${ branch }

jobs:
  build-and-deploy:
    runs-on: windows-latest

    steps:
    - uses: actions/checkout@v2

    - name: Set up Python version
      uses: actions/setup-python@v1
      with:
        python-version: '${ runtimeStackVersion }'

    - name: Install Python dependencies
      run: |
        python3 -m venv env
        .\\env\\Scripts\\activate
        pip install -r requirements.txt

    - name: Zip the application files
      run: Compress-Archive .\\* app.zip

    - name: 'Deploy to Azure Web App'
      uses: azure/webapps-deploy@v2
      with:
        app-name: '${ siteName }'
        slot-name: '${ slot }'
        publish-profile: \${{ secrets.${ secretName } }}
        package: '.\\myapp.zip'` ; } private _getPythonGithubActionWorkflowDefinitionForLinux ( siteName , slotName , branch , secretName , runtimeStackVersion ) { const webAppName = slotName ? `${ siteName }(${ slotName })` : siteName ; const slot = slotName || 'production' ; return `# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
# More GitHub Actions for Azure: https://github.com/Azure/actions
# More info on Python, GitHub Actions, and Azure App Service: https://aka.ms/python-webapps-actions

name: Build and deploy Python app to Azure Web App - ${ webAppName }

on:
  push:
    branches:
      - ${ branch }
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2

    - name: Set up Python version
      uses: actions/setup-python@v1
      with:
        python-version: '${ runtimeStackVersion }'

    - name: Create and start virtual environment
      run: |
        python -m venv venv
        source venv/bin/activate
    
    - name: Install dependencies
      run: pip install -r requirements.txt
      
    # Optional: Add step to run tests here (PyTest, Django test suites, etc.)

    - name: Upload artifact for deployment jobs
      uses: actions/upload-artifact@v2
      with:
        name: python-app
        path: |
          . 
          !venv/

  deploy:
    runs-on: ubuntu-latest
    needs: build
    environment:
      name: '${ slot }'
      url: \${{ steps.deploy-to-webapp.outputs.webapp-url }}

    steps:
      - name: Download artifact from build job
        uses: actions/download-artifact@v2
        with:
          name: python-app
          path: .

    - name: 'Deploy to Azure Web App'
      uses: azure/webapps-deploy@v2
      id: deploy-to-webapp
      with:
        app-name: '${ siteName }'
        slot-name: '${ slot }'
        publish-profile: \${{ secrets.${ secretName } }}` ; } private _getDotnetCoreGithubActionWorkflowDefinition ( siteName , slotName , branch , isLinuxApp , secretName , runtimeStackVersion ) { const webAppName = slotName ? `${ siteName }(${ slotName })` : siteName ; const slot = slotName || 'production' ; return `# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
# More GitHub Actions for Azure: https://github.com/Azure/actions

name: Build and deploy ASP.Net Core app to Azure Web App - ${ webAppName }

on:
  push:
    branches:
      - ${ branch }

jobs:
  build-and-deploy:
    runs-on: ${ isLinuxApp ? 'ubuntu-latest' : 'windows-latest' }

    steps:
    - uses: actions/checkout@v2

    - name: Set up .NET Core
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: '${ runtimeStackVersion }'

    - name: Build with dotnet
      run: dotnet build --configuration Release

    - name: dotnet publish
      run: dotnet publish -c Release -o \${{env.DOTNET_ROOT}}/myapp

    - name: Deploy to Azure Web App
      id: deploy-to-webapp
      uses: azure/webapps-deploy@v2
      with:
        app-name: '${ siteName }'
        slot-name: '${ slot }'
        publish-profile: \${{ secrets.${ secretName } }}
        package: \${{env.DOTNET_ROOT}}/myapp ` ; } private _getJavaJarGithubActionWorkflowDefinition ( siteName , slotName , branch , isLinuxApp , secretName , runtimeStackVersion ) { const webAppName = slotName ? `${ siteName }(${ slotName })` : siteName ; const slot = slotName || 'production' ; return `# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
# More GitHub Actions for Azure: https://github.com/Azure/actions

name: Build and deploy JAR app to Azure Web App - ${ webAppName }

on:
  push:
    branches:
      - ${ branch }

jobs:
  build-and-deploy:
    runs-on: ${ isLinuxApp ? 'ubuntu-latest' : 'windows-latest' }

    steps:
    - uses: actions/checkout@v2

    - name: Set up Java version
      uses: actions/setup-java@v1
      with:
        java-version: '${ runtimeStackVersion }'

    - name: Build with Maven
      run: mvn clean install

    - name: Deploy to Azure Web App
      id: deploy-to-webapp
      uses: azure/webapps-deploy@v2
      with:
        app-name: '${ siteName }'
        slot-name: '${ slot }'
        publish-profile: \${{ secrets.${ secretName } }}
        package: '\${{ github.workspace }}/target/*.jar'` ; } private _getJavaWarGithubActionWorkflowDefinition ( siteName , slotName , branch , isLinuxApp , secretName , runtimeStackVersion ) { const webAppName = slotName ? `${ siteName }(${ slotName })` : siteName ; const slot = slotName || 'production' ; return `# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
# More GitHub Actions for Azure: https://github.com/Azure/actions

name: Build and deploy WAR app to Azure Web App - ${ webAppName }

on:
  push:
    branches:
      - ${ branch }

jobs:
  build-and-deploy:
    runs-on: ${ isLinuxApp ? 'ubuntu-latest' : 'windows-latest' }

    steps:
    - uses: actions/checkout@v2

    - name: Set up Java version
      uses: actions/setup-java@v1
      with:
        java-version: '${ runtimeStackVersion }'

    - name: Build with Maven
      run: mvn clean install

    - name: Deploy to Azure Web App
      id: deploy-to-webapp
      uses: azure/webapps-deploy@v2
      with:
        app-name: '${ siteName }'
        slot-name: '${ slot }'
        publish-profile: \${{ secrets.${ secretName } }}
        package: '\${{ github.workspace }}/target/*.war'` ; } private _getAspNetGithubActionWorkflowDefinition ( siteName , slotName , branch , secretName , runtimeStackVersion ) { const webAppName = slotName ? `${ siteName }(${ slotName })` : siteName ; const slot = slotName || 'production' ; return `# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
# More GitHub Actions for Azure: https://github.com/Azure/actions

name: Build and deploy ASP app to Azure Web App - ${ webAppName }

on:
  push:
    branches:
      - ${ branch }

jobs:
  build-and-deploy:
    runs-on: 'windows-latest'

    steps:
    - uses: actions/checkout@v2

    - name: Setup MSBuild path
      uses: microsoft/setup-msbuild@v1.0.0

    - name: Setup NuGet
      uses: NuGet/setup-nuget@v1.0.2

    - name: Restore NuGet packages
      run: nuget restore

    - name: Publish to folder
      run: msbuild /p:Configuration=Release /p:DeployOnBuild=true /t:WebPublish /p:WebPublishMethod=FileSystem /p:publishUrl=./published/ /p:PackageAsSingleFile=false

    - name: Deploy to Azure Web App
      id: deploy-to-webapp
      uses: azure/webapps-deploy@v2
      with:
        app-name: '${ siteName }'
        slot-name: '${ slot }'
        publish-profile: \${{ secrets.${ secretName } }}
        package: ./published/` ; } }