diff --git a/README.md b/README.md index 4b5b9f8..f39892f 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,8 @@ These are the workflow files for helping people get started with GitHub Actions. ### Directory structure -* [ci](ci): solutions for Continuous Integration and Deployments +* [ci](ci): solutions for Continuous Integration workflows. +* [deployments](deployments): solutions for Deployment workflows. * [automation](automation): solutions for automating workflows. * [code-scanning](code-scanning): starter workflows for [Code Scanning](https://github.com/features/security) * [icons](icons): svg icons for the relevant template @@ -44,5 +45,5 @@ For example: `ci/django.yml` and `ci/properties/django.properties.json`. These variables can be placed in the starter workflow and will be substituted as detailed below: * `$default-branch`: will substitute the branch from the repository, for example `main` and `master` -* `$protected-branches`: will substitue any protected branches from the repository. +* `$protected-branches`: will substitute any protected branches from the repository * `$cron-daily`: will substitute a valid but random time within the day diff --git a/ci/objective-c-xcode.yml b/ci/objective-c-xcode.yml new file mode 100644 index 0000000..db009b0 --- /dev/null +++ b/ci/objective-c-xcode.yml @@ -0,0 +1,30 @@ +name: Xcode - Build and Analyze + +on: + push: + branches: [ $default-branch ] + pull_request: + branches: [ $default-branch ] + +jobs: + build: + name: Build and analyse default scheme using xcodebuild command + runs-on: macos-latest + + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Set Default Scheme + run: | + scheme_list=$(xcodebuild -list -json | tr -d "\n") + default=$(echo $scheme_list | ruby -e "require 'json'; puts JSON.parse(STDIN.gets)['project']['targets'][0]") + echo $default | cat >default + echo Using default scheme: $default + - name: Build + env: + scheme: ${{ 'default' }} + run: | + if [ $scheme = default ]; then scheme=$(cat default); fi + if [ "`ls -A | grep -i \\.xcworkspace\$`" ]; then filetype_parameter="workspace" && file_to_build="`ls -A | grep -i \\.xcworkspace\$`"; else filetype_parameter="project" && file_to_build="`ls -A | grep -i \\.xcodeproj\$`"; fi + file_to_build=`echo $file_to_build | awk '{$1=$1;print}'` + xcodebuild clean build analyze -scheme "$scheme" -"$filetype_parameter" "$file_to_build" | xcpretty && exit ${PIPESTATUS[0]} diff --git a/ci/properties/node.js.properties.json b/ci/properties/node.js.properties.json index 32f5306..7f68d88 100644 --- a/ci/properties/node.js.properties.json +++ b/ci/properties/node.js.properties.json @@ -2,5 +2,5 @@ "name": "Node.js", "description": "Build and test a Node.js project with npm.", "iconName": "nodejs", - "categories": ["Continuous integration", "JavaScript", "npm", "React", "Angular"] + "categories": ["Continuous integration", "JavaScript", "npm", "React", "Angular", "Vue"] } diff --git a/ci/properties/objective-c-xcode.properties.json b/ci/properties/objective-c-xcode.properties.json new file mode 100644 index 0000000..e6068fe --- /dev/null +++ b/ci/properties/objective-c-xcode.properties.json @@ -0,0 +1,6 @@ +{ + "name": "Xcode - Build and Analyze", + "description": "Build Xcode project using xcodebuild", + "iconName": "xcode", + "categories": ["Continuous integration", "Xcode", "Objective-C"] +} diff --git a/ci/properties/super-linter.properties.json b/ci/properties/super-linter.properties.json new file mode 100644 index 0000000..0be52e1 --- /dev/null +++ b/ci/properties/super-linter.properties.json @@ -0,0 +1,6 @@ +{ + "name": "Super Linter - Run Linters for several languages", + "description": "Run linters for several languages on your code base for changed files", + "iconName": "octicon check-circle", + "categories": ["Continuous integration", "code-quality", "code-review"] +} diff --git a/ci/properties/symfony.properties.json b/ci/properties/symfony.properties.json new file mode 100644 index 0000000..ea9a950 --- /dev/null +++ b/ci/properties/symfony.properties.json @@ -0,0 +1,10 @@ +{ + "name": "Symfony", + "description": "Test a Symfony project.", + "iconName": "php", + "categories": [ + "Continuous integration", + "PHP", + "Symfony" + ] +} \ No newline at end of file diff --git a/ci/super-linter.yml b/ci/super-linter.yml new file mode 100644 index 0000000..bebd82d --- /dev/null +++ b/ci/super-linter.yml @@ -0,0 +1,29 @@ +# This workflow executes several linters on changed files based on languages used in your code base whenever +# you push a code or open a pull request. +# +# You can adjust the behavior by modifying this file. +# For more information, see: +# https://github.com/github/super-linter +name: Lint Code Base + +on: + push: + branches: [ $default-branch ] + pull_request: + branches: [ $default-branch ] +jobs: + run-lint: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + # Full git history is needed to get a proper list of changed files within `super-linter` + fetch-depth: 0 + + - name: Lint Code Base + uses: github/super-linter@v4 + env: + VALIDATE_ALL_CODEBASE: false + DEFAULT_BRANCH: $default-branch + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/ci/symfony.yml b/ci/symfony.yml new file mode 100644 index 0000000..7d1ca74 --- /dev/null +++ b/ci/symfony.yml @@ -0,0 +1,39 @@ +name: Symfony + +on: + push: + branches: [ $default-branch ] + pull_request: + branches: [ $default-branch ] + +jobs: + symfony-tests: + runs-on: ubuntu-latest + steps: + # To automatically get bug fixes and new Php versions for shivammathur/setup-php, + # change this to (see https://github.com/shivammathur/setup-php#bookmark-versioning): + # uses: shivammathur/setup-php@v2 + - uses: shivammathur/setup-php@2cb9b829437ee246e9b3cac53555a39208ca6d28 + with: + php-version: '8.0' + - uses: actions/checkout@v2 + - name: Copy .env.test.local + run: php -r "file_exists('.env.test.local') || copy('.env.test', '.env.test.local');" + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v2 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + - name: Install Dependencies + run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist + - name: Create Database + run: | + mkdir -p data + touch data/database.sqlite + - name: Execute tests (Unit and Feature tests) via PHPUnit + env: + DATABASE_URL: sqlite:///%kernel.project_dir%/data/database.sqlite + run: vendor/bin/phpunit diff --git a/code-scanning/cloudrail.yml b/code-scanning/cloudrail.yml new file mode 100644 index 0000000..9f95c5c --- /dev/null +++ b/code-scanning/cloudrail.yml @@ -0,0 +1,58 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +name: cloudrail + +on: + push: + branches: [ $default-branch, $protected-branches ] + pull_request: + branches: [ $default-branch ] + schedule: + - cron: $cron-weekly + +jobs: + cloudrail: + name: Run Indeni Cloudrail on Terraform code with SARIF output + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + steps: + - name: Clone repo + uses: actions/checkout@v2 + + # For Terraform, Cloudrail requires the plan as input. So we generate it using + # the Terraform core binary. + - uses: hashicorp/setup-terraform@v1 + with: + terraform_version: v0.13.2 + + - run: terraform init + + - run: terraform plan -out=plan.out + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + + # Confirm we have the plan file + - run: stat plan.out + + - name: Run Cloudrail + uses: indeni/cloudrail-run-ga@b56ed2d30913c975b36df231adc2eabf05523622 + with: + tf-plan-file: plan.out # This was created in a "terraform plan" step + cloudrail-api-key: ${{ secrets.CLOUDRAIL_API_KEY }} # This requires registration to Indeni Cloudrail's SaaS at https://web.cloudrail.app + cloud-account-id: # Leave this empty for Static Analaysis, or provide an account ID for Dynamic Analysis, see instructions in Cloudrail SaaS + + - name: Upload SARIF file + uses: github/codeql-action/upload-sarif@v1 + # Remember that if issues are found, Cloudrail return non-zero exit code, so the if: always() + # is needed to ensure the SARIF file is uploaded + if: always() + with: + sarif_file: cloudrail_results.sarif diff --git a/code-scanning/mayhem-for-api.yml b/code-scanning/mayhem-for-api.yml index 0aab0b4..59d66a0 100644 --- a/code-scanning/mayhem-for-api.yml +++ b/code-scanning/mayhem-for-api.yml @@ -10,7 +10,7 @@ # To use this workflow, you will need to: # # 1. Create a Mayhem for API account at -# https://mayhem4api.forallsecure.com/signup (30-day free trial) +# https://mayhem4api.forallsecure.com/signup # # 2. Create a service account token `mapi organization service-account create # ` diff --git a/code-scanning/properties/cloudrail.properties.json b/code-scanning/properties/cloudrail.properties.json new file mode 100644 index 0000000..830d966 --- /dev/null +++ b/code-scanning/properties/cloudrail.properties.json @@ -0,0 +1,7 @@ +{ + "name": "cloudrail", + "creator": "Indeni Cloudrail", + "description": "Cloudrail can be used to scan your infrastructure-as-code files for potential security and compliance issues. The Cloudrail action is often used as part of both CI workflows (on pull_request) and on CD workflows to identify potential issues.", + "iconName": "cloudrail", + "categories": ["Code Scanning", "HCL"] +} diff --git a/deployments/aws.yml b/deployments/aws.yml index 8b10116..6a6643c 100644 --- a/deployments/aws.yml +++ b/deployments/aws.yml @@ -5,29 +5,40 @@ # # 1. Create an ECR repository to store your images. # For example: `aws ecr create-repository --repository-name my-ecr-repo --region us-east-2`. -# Replace the value of `ECR_REPOSITORY` in the workflow below with your repository's name. -# Replace the value of `aws-region` in the workflow below with your repository's region. +# Replace the value of the `ECR_REPOSITORY` environment variable in the workflow below with your repository's name. +# Replace the value of the `AWS_REGION` environment variable in the workflow below with your repository's region. # # 2. Create an ECS task definition, an ECS cluster, and an ECS service. # For example, follow the Getting Started guide on the ECS console: # https://us-east-2.console.aws.amazon.com/ecs/home?region=us-east-2#/firstRun -# Replace the values for `service` and `cluster` in the workflow below with your service and cluster names. +# Replace the value of the `ECS_SERVICE` environment variable in the workflow below with the name you set for the Amazon ECS service. +# Replace the value of the `ECS_CLUSTER` environment variable in the workflow below with the name you set for the cluster. # # 3. Store your ECS task definition as a JSON file in your repository. # The format should follow the output of `aws ecs register-task-definition --generate-cli-skeleton`. -# Replace the value of `task-definition` in the workflow below with your JSON file's name. -# Replace the value of `container-name` in the workflow below with the name of the container +# Replace the value of the `ECS_TASK_DEFINITION` environment variable in the workflow below with the path to the JSON file. +# Replace the value of the `CONTAINER_NAME` environment variable in the workflow below with the name of the container # in the `containerDefinitions` section of the task definition. # # 4. Store an IAM user access key in GitHub Actions secrets named `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`. # See the documentation for each action used below for the recommended IAM policies for this IAM user, # and best practices on handling the access key credentials. +name: Deploy to Amazon ECS + on: release: types: [created] -name: Deploy to Amazon ECS +env: + AWS_REGION: MY_AWS_REGION # set this to your preferred AWS region, e.g. us-west-1 + ECR_REPOSITORY: MY_ECR_REPOSITORY # set this to your Amazon ECR repository name + ECS_SERVICE: MY_ECS_SERVICE # set this to your Amazon ECS service name + ECS_CLUSTER: MY_ECS_CLUSTER # set this to your Amazon ECS cluster name + ECS_TASK_DEFINITION: MY_ECS_TASK_DEFINITION # set this to the path to your Amazon ECS task definition + # file, e.g. .aws/task-definition.json + CONTAINER_NAME: MY_CONTAINER_NAME # set this to the name of the container in the + # containerDefinitions section of your task definition jobs: deploy: @@ -44,7 +55,7 @@ jobs: with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: us-east-2 + aws-region: ${{ env.AWS_REGION }} - name: Login to Amazon ECR id: login-ecr @@ -54,7 +65,6 @@ jobs: id: build-image env: ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} - ECR_REPOSITORY: my-ecr-repo IMAGE_TAG: ${{ github.sha }} run: | # Build a docker container and @@ -68,14 +78,14 @@ jobs: id: task-def uses: aws-actions/amazon-ecs-render-task-definition@v1 with: - task-definition: task-definition.json - container-name: sample-app + task-definition: ${{ env.ECS_TASK_DEFINITION }} + container-name: ${{ env.CONTAINER_NAME }} image: ${{ steps.build-image.outputs.image }} - name: Deploy Amazon ECS task definition uses: aws-actions/amazon-ecs-deploy-task-definition@v1 with: task-definition: ${{ steps.task-def.outputs.task-definition }} - service: sample-app-service - cluster: default + service: ${{ env.ECS_SERVICE }} + cluster: ${{ env.ECS_CLUSTER }} wait-for-service-stability: true diff --git a/deployments/properties/alibabacloud.properties.json b/deployments/properties/alibabacloud.properties.json index 1ce285f..d416d88 100644 --- a/deployments/properties/alibabacloud.properties.json +++ b/deployments/properties/alibabacloud.properties.json @@ -3,5 +3,5 @@ "description": "Deploy a container to Alibaba Cloud Container Service for Kubernetes (ACK).", "creator": "Alibaba Cloud", "iconName": "alibabacloud", - "categories": ["Deployment"] + "categories": ["Deployment", "Dockerfile"] } diff --git a/deployments/properties/google.properties.json b/deployments/properties/google.properties.json index c7f216f..6318106 100644 --- a/deployments/properties/google.properties.json +++ b/deployments/properties/google.properties.json @@ -3,5 +3,5 @@ "description": "Build a docker container, publish it to Google Container Registry, and deploy to GKE.", "creator": "Google Cloud", "iconName": "googlegke", - "categories": ["Deployment"] + "categories": ["Deployment", "Dockerfile"] } \ No newline at end of file diff --git a/deployments/properties/ibm.properties.json b/deployments/properties/ibm.properties.json index 4219690..8e5b047 100644 --- a/deployments/properties/ibm.properties.json +++ b/deployments/properties/ibm.properties.json @@ -3,5 +3,5 @@ "description": "Build a docker container, publish it to IBM Cloud Container Registry, and deploy to IBM Cloud Kubernetes Service.", "creator": "IBM", "iconName": "ibm", - "categories": ["Deployment"] + "categories": ["Deployment", "Dockerfile"] } \ No newline at end of file diff --git a/deployments/properties/tencent.properties.json b/deployments/properties/tencent.properties.json index 32d0da7..df18356 100644 --- a/deployments/properties/tencent.properties.json +++ b/deployments/properties/tencent.properties.json @@ -3,5 +3,5 @@ "description": "This workflow will build a docker container, publish and deploy it to Tencent Kubernetes Engine (TKE).", "creator": "Tencent Cloud", "iconName": "tencentcloud", - "categories": ["Deployment"] + "categories": ["Deployment", "Dockerfile"] } \ No newline at end of file diff --git a/icons/cloudrail.svg b/icons/cloudrail.svg new file mode 100644 index 0000000..9aaf9c3 --- /dev/null +++ b/icons/cloudrail.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + diff --git a/script/sync-ghes/settings.json b/script/sync-ghes/settings.json index e4f50fa..9648ab4 100644 --- a/script/sync-ghes/settings.json +++ b/script/sync-ghes/settings.json @@ -2,7 +2,6 @@ "folders": [ "../../ci", "../../automation", - "../../deployments", "../../code-scanning" ], "enabledActions": [ diff --git a/script/validate-data/index.ts b/script/validate-data/index.ts index 8413653..7dce3d1 100755 --- a/script/validate-data/index.ts +++ b/script/validate-data/index.ts @@ -40,7 +40,7 @@ const propertiesSchema = { } } -async function checkWorkflows(folders: string[]): Promise { +async function checkWorkflows(folders: string[], allowed_categories: string[]): Promise { const result: WorkflowWithErrors[] = [] const workflow_template_names = new Set() for (const folder of folders) { @@ -49,13 +49,13 @@ async function checkWorkflows(folders: string[]): Promise }); for (const e of dir) { - if (e.isFile()) { + if (e.isFile() && [".yml", ".yaml"].includes(extname(e.name))) { const fileType = basename(e.name, extname(e.name)) const workflowFilePath = join(folder, e.name); const propertiesFilePath = join(folder, "properties", `${fileType}.properties.json`) - const workflowWithErrors = await checkWorkflow(workflowFilePath, propertiesFilePath); + const workflowWithErrors = await checkWorkflow(workflowFilePath, propertiesFilePath, allowed_categories); if(workflowWithErrors.name && workflow_template_names.size == workflow_template_names.add(workflowWithErrors.name).size) { workflowWithErrors.errors.push(`Workflow template name "${workflowWithErrors.name}" already exists`) } @@ -69,13 +69,12 @@ async function checkWorkflows(folders: string[]): Promise return result; } -async function checkWorkflow(workflowPath: string, propertiesPath: string): Promise { +async function checkWorkflow(workflowPath: string, propertiesPath: string, allowed_categories: string[]): Promise { let workflowErrors: WorkflowWithErrors = { id: workflowPath, name: null, errors: [] } - try { const workflowFileContent = await fs.readFile(workflowPath, "utf8"); safeLoad(workflowFileContent); // Validate yaml parses without error @@ -105,6 +104,10 @@ async function checkWorkflow(workflowPath: string, propertiesPath: string): Prom } } + if (!workflowPath.endsWith("blank.yml") && (!properties.categories || + !properties.categories.some(category => allowed_categories.some(ac => ac.toLowerCase() == category.toLowerCase())))) { + workflowErrors.errors.push(`Workflow does not contain at least one allowed category - ${allowed_categories}`) + } } catch (e) { workflowErrors.errors.push(e.toString()) } @@ -115,7 +118,7 @@ async function checkWorkflow(workflowPath: string, propertiesPath: string): Prom try { const settings = require("./settings.json"); const erroredWorkflows = await checkWorkflows( - settings.folders + settings.folders, settings.allowed_categories ) if (erroredWorkflows.length > 0) { diff --git a/script/validate-data/settings.json b/script/validate-data/settings.json index f7c08cf..ce89e36 100644 --- a/script/validate-data/settings.json +++ b/script/validate-data/settings.json @@ -2,6 +2,13 @@ "folders": [ "../../ci", "../../automation", - "../../deployments" + "../../deployments", + "../../code-scanning" + ], + "allowed_categories" : [ + "Continuous integration", + "Deployment", + "Code Scanning", + "Automation" ] } \ No newline at end of file