Updating distribution
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
---
|
||||
name: Azure DevOps
|
||||
about: Migrate Azure DevOps pipelines to GitHub Actions with Valet
|
||||
title: "[Azure DevOps]:"
|
||||
labels: azure-devops
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
## Inputs
|
||||
|
||||
Provide the following required inputs:
|
||||
|
||||
Organization:
|
||||
_The Azure DevOps organization to migrate pipelines from._
|
||||
|
||||
Project:
|
||||
_The Azure DevOps project to migrate pipelines from._
|
||||
|
||||
## Available commands
|
||||
|
||||
The following commands can be executed by adding a comment to this issue:
|
||||
|
||||
- `/audit`
|
||||
- `/dry-run --pipeline-type pipeline|release --pipeline-id :pipeline-id`
|
||||
- `/migrate --pipeline-type pipeline|release --pipeline-id :pipeline-id --target-url :github-repository-url`
|
||||
|
||||
**Note:** The `pipeline-type` option will default to `pipeline` if omitted. If any remaining options are missing, the command will not be successful.
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: Circle CI
|
||||
about: Migrate Circle CI pipelines to GitHub Actions with Valet
|
||||
title: "[Circle CI]:"
|
||||
labels: circle-ci
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
## Inputs
|
||||
|
||||
Provide the following required inputs:
|
||||
|
||||
Organization:
|
||||
_The Circle CI organization to migrate pipelines from._
|
||||
|
||||
## Available commands
|
||||
|
||||
The following commands can be executed by adding a comment to this issue:
|
||||
|
||||
- `/audit`
|
||||
- `/dry-run --repository :repository-name`
|
||||
- `/migrate --repository :repository-name --target-url :github-repository-url`
|
||||
|
||||
**Note**: If any options are missing, the command will not be successful.
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: GitLab CI
|
||||
about: Migrate GitLab CI pipelines to GitHub Actions with Valet
|
||||
title: "[GitLab CI]:"
|
||||
labels: gitlab
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
## Inputs
|
||||
|
||||
Provide the following required inputs:
|
||||
|
||||
Namespace:
|
||||
_The GitLab CI namespace (or group) to migrate pipelines from._
|
||||
|
||||
## Available commands
|
||||
|
||||
The following commands can be executed by adding a comment to this issue:
|
||||
|
||||
- `/audit`
|
||||
- `/dry-run --project :project-name`
|
||||
- `/migrate --project :project-name --target-url :github-repository-url`
|
||||
|
||||
**Note**: If any options are missing, the command will not be successful.
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: Jenkins
|
||||
about: Migrate Jenkins jobs to GitHub Actions with Valet
|
||||
title: "[Jenkins]:"
|
||||
labels: jenkins
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
## Inputs
|
||||
|
||||
Provide the following optional inputs:
|
||||
|
||||
Folders:
|
||||
_Include specific folders in an audit_
|
||||
|
||||
## Available commands
|
||||
|
||||
The following commands can be executed by adding a comment to this issue:
|
||||
|
||||
- `/audit`
|
||||
- `/dry-run --source-url :jenkins-job-url`
|
||||
- `/migrate --source-url :jenkins-job-url --target-url :github-repository-url`
|
||||
|
||||
**Note**: If any options are missing, the command will not be successful.
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: Travis CI
|
||||
about: Migrate Travis CI pipelines to GitHub Actions with Valet
|
||||
title: "[Travis CI]:"
|
||||
labels: travis-ci
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
## Inputs
|
||||
|
||||
Provide the following required inputs:
|
||||
|
||||
Organization:
|
||||
_The Travis CI organization to migrate pipelines from._
|
||||
|
||||
## Available commands
|
||||
|
||||
The following commands can be executed by adding a comment to this issue:
|
||||
|
||||
- `/audit`
|
||||
- `/dry-run --repository :repository-name`
|
||||
- `/migrate --repository :repository-name --target-url :github-repository-url`
|
||||
|
||||
**Note**: If any options are missing, the command will not be successful.
|
||||
@@ -0,0 +1,33 @@
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
ruby_version: 2.7.1
|
||||
|
||||
jobs:
|
||||
unit-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: ${{ env.ruby_version }}
|
||||
- name: Install dependencies
|
||||
run: bundle install
|
||||
- name: Run specs
|
||||
run: bin/rspec
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ruby/setup-ruby@v1.47.0
|
||||
with:
|
||||
ruby-version: ${{ env.ruby_version }}
|
||||
- name: Install dependencies
|
||||
run: bundle install
|
||||
- name: Lint
|
||||
run: bin/rubocop
|
||||
@@ -0,0 +1,227 @@
|
||||
name: valet-issue-ops
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
env:
|
||||
GITHUB_INSTANCE_URL: https://github.com
|
||||
GITHUB_ACCESS_TOKEN: ${{ secrets.GH_ACCESS_TOKEN }}
|
||||
JENKINS_INSTANCE_URL: https://jenkout.westus2.cloudapp.azure.com
|
||||
JENKINS_USERNAME: ${{ secrets.jenkins_username }}
|
||||
JENKINS_ACCESS_TOKEN: ${{ secrets.jenkins_access_token }}
|
||||
JENKINSFILE_ACCESS_TOKEN: ${{ secrets.jenkinsfile_access_token }}
|
||||
AZURE_DEVOPS_ACCESS_TOKEN: ${{ secrets.azure_devops_access_token }}
|
||||
TRAVIS_CI_ACCESS_TOKEN: ${{ secrets.travis_ci_access_token }}
|
||||
TRAVIS_CI_SOURCE_GITHUB_ACCESS_TOKEN: ${{ secrets.travis_ci_source_github_access_token }}
|
||||
GITLAB_ACCESS_TOKEN: ${{ secrets.gitlab_access_token }}
|
||||
CIRCLE_CI_ACCESS_TOKEN: ${{ secrets.circle_ci_access_token }}
|
||||
CIRCLE_CI_SOURCE_GITHUB_ACCESS_TOKEN: ${{ secrets.circle_ci_source_github_access_token }}
|
||||
|
||||
jobs:
|
||||
execute-valet:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
command: ${{ steps.prepare.outputs.command }}
|
||||
log-filename: ${{ steps.logs.outputs.filename }}
|
||||
container:
|
||||
image: ghcr.io/valet-customers/valet-cli:latest
|
||||
credentials:
|
||||
username: ${{ secrets.valet_ghcr_username }}
|
||||
password: ${{ secrets.valet_ghcr_password }}
|
||||
steps:
|
||||
- uses: actions-ecosystem/action-add-labels@v1
|
||||
if: always()
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
labels: valet-running
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install dependencies
|
||||
run: bundle install --without development
|
||||
- name: Prepare arguments
|
||||
id: prepare
|
||||
run: |
|
||||
echo "${{ toJSON(github.event.issue.labels.*.name) }}"
|
||||
./bin/parse_issue "${{ github.event.issue.body }}" "${{ github.event.comment.body }}" "${{ toJSON(github.event.issue.labels.*.name) }}"
|
||||
- name: Validate arguments
|
||||
run: |
|
||||
if [ -z "${{ steps.prepare.outputs.provider }}" ]; then
|
||||
echo "Unable to determine provider"
|
||||
exit 1
|
||||
elif [ -z "${{ steps.prepare.outputs.command }}" ]; then
|
||||
echo "Unable to determine command"
|
||||
exit 1
|
||||
fi
|
||||
- name: execute valet
|
||||
run: |
|
||||
valet ${{ steps.prepare.outputs.command }} ${{ steps.prepare.outputs.provider }} \
|
||||
${{ steps.prepare.outputs.args }} \
|
||||
--output-dir /data/output \
|
||||
--no-telemetry
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: always()
|
||||
with:
|
||||
path: /data/output/
|
||||
name: output
|
||||
- if: always()
|
||||
id: logs
|
||||
run: |
|
||||
path=$(ls /data/output/log/*.log | head -1)
|
||||
filename=$(basename "$path")
|
||||
echo "LOG_FILE_PATH=$path" >> $GITHUB_ENV
|
||||
echo "::set-output name=filename::$filename"
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: always()
|
||||
with:
|
||||
path: ${{ env.LOG_FILE_PATH }}
|
||||
name: logs
|
||||
|
||||
audit:
|
||||
runs-on: ubuntu-latest
|
||||
if: needs.execute-valet.outputs.command == 'audit'
|
||||
needs: execute-valet
|
||||
steps:
|
||||
- uses: actions/download-artifact@v2
|
||||
if: always()
|
||||
with:
|
||||
name: output
|
||||
- uses: actions/github-script@v5
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs')
|
||||
const summaryText = fs.readFileSync("./audit_summary.md", "utf8")
|
||||
const artifactUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${process.env.GITHUB_RUN_ID}`
|
||||
const body = `Audit successfully completed :rocket:
|
||||
|
||||
<details>
|
||||
<summary>Audit summary :point_down:</summary>
|
||||
|
||||
\`\`\`
|
||||
${summaryText}
|
||||
\`\`\`
|
||||
|
||||
</details>
|
||||
|
||||
Download full results [here](${artifactUrl})
|
||||
`
|
||||
await github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body
|
||||
})
|
||||
dry-run:
|
||||
runs-on: ubuntu-latest
|
||||
if: needs.execute-valet.outputs.command == 'dry-run'
|
||||
needs: execute-valet
|
||||
steps:
|
||||
- uses: actions/download-artifact@v2
|
||||
if: always()
|
||||
with:
|
||||
name: output
|
||||
- uses: actions/github-script@v5
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs')
|
||||
const directory = "${{ github.workspace }}/"
|
||||
const globber = await glob.create(`${directory}**/*.yml`)
|
||||
|
||||
const workflows = []
|
||||
for await (const file of globber.globGenerator()) {
|
||||
const content = fs.readFileSync(file, 'utf8')
|
||||
workflows.push(
|
||||
"<details>",
|
||||
` <summary>${file.substring(directory.length)}</summary>`,
|
||||
"",
|
||||
"```yaml",
|
||||
content,
|
||||
"```",
|
||||
"</details>",
|
||||
""
|
||||
);
|
||||
}
|
||||
|
||||
const body = `Dry run was successful :boom:
|
||||
|
||||
Transformed workflows:
|
||||
|
||||
${workflows.join("\n")}
|
||||
`
|
||||
await github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body
|
||||
})
|
||||
|
||||
migrate:
|
||||
runs-on: ubuntu-latest
|
||||
if: needs.execute-valet.outputs.command == 'migrate'
|
||||
needs: execute-valet
|
||||
steps:
|
||||
- uses: actions/download-artifact@v2
|
||||
if: always()
|
||||
with:
|
||||
name: logs
|
||||
- id: pull-request-url
|
||||
run: |
|
||||
pullRequest=$(grep "${{ env.pullRequestPattern }}" ${{ needs.execute-valet.outputs.log-filename }} | sed -rn "s/^.*${{ env.pullRequestPattern }}'(.+)'.*$/\1/p")
|
||||
echo $pullRequest
|
||||
echo ::set-output name=output::$pullRequest
|
||||
env:
|
||||
pullRequestPattern: "Pull request: "
|
||||
- uses: actions/github-script@v5
|
||||
env:
|
||||
PULL_REQUEST_URL: "${{ steps.pull-request-url.outputs.output }}"
|
||||
with:
|
||||
script: |
|
||||
const body = `Migration was successful :sparkles:
|
||||
|
||||
Continue to the [pull request](${process.env.PULL_REQUEST_URL}) to complete the migration.
|
||||
`
|
||||
try {
|
||||
await github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body
|
||||
})
|
||||
} catch(e) {
|
||||
console.log(e)
|
||||
}
|
||||
|
||||
cleanup:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [execute-valet, audit, migrate, dry-run]
|
||||
if: always()
|
||||
steps:
|
||||
- uses: actions/download-artifact@v2
|
||||
if: always() && needs.execute-valet.result == 'failure'
|
||||
with:
|
||||
name: logs
|
||||
- uses: actions/github-script@v5
|
||||
if: always() && needs.execute-valet.result == 'failure'
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs')
|
||||
const body = `Something went wrong. Please check the logs for more information.
|
||||
|
||||
<details>
|
||||
<summary>Logs :point_down:</summary>
|
||||
|
||||
\`\`\`
|
||||
${fs.readFileSync("${{ needs.execute-valet.outputs.log-filename }}", "utf8")}
|
||||
\`\`\`
|
||||
</details>
|
||||
`
|
||||
await github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body
|
||||
})
|
||||
- uses: actions-ecosystem/action-remove-labels@v1
|
||||
if: always()
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
labels: valet-running
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/.bundle/
|
||||
/.yardoc
|
||||
/_yardoc/
|
||||
/coverage/
|
||||
/doc/
|
||||
/pkg/
|
||||
/spec/reports/
|
||||
/tmp/
|
||||
/log/
|
||||
/vendor/
|
||||
|
||||
.DS_Store
|
||||
|
||||
# rspec failure tracking
|
||||
.rspec_status
|
||||
|
||||
.env*local
|
||||
|
||||
*.gem
|
||||
.vscode/settings.json
|
||||
@@ -0,0 +1,5 @@
|
||||
--format progress
|
||||
--color
|
||||
--require spec_helper
|
||||
--format RSpec::Github::Formatter
|
||||
--pattern "**/*_spec.rb"
|
||||
@@ -0,0 +1,37 @@
|
||||
inherit_gem:
|
||||
rubocop-github:
|
||||
- config/default_edge.yml
|
||||
|
||||
Lint/AmbiguousBlockAssociation:
|
||||
Exclude:
|
||||
- "spec/**/*"
|
||||
Style/HashEachMethods:
|
||||
Enabled: true
|
||||
Style/HashTransformKeys:
|
||||
Enabled: true
|
||||
Style/HashTransformValues:
|
||||
Enabled: true
|
||||
Style/Documentation:
|
||||
Enabled: false
|
||||
Naming/MethodParameterName:
|
||||
Enabled: false
|
||||
Style/MultilineIfModifier:
|
||||
Enabled: false
|
||||
Performance/Detect:
|
||||
Enabled: false
|
||||
Layout/FirstArrayElementLineBreak:
|
||||
Enabled: true
|
||||
Layout/FirstHashElementIndentation:
|
||||
EnforcedStyle: consistent
|
||||
Layout/FirstHashElementLineBreak:
|
||||
Enabled: true
|
||||
Layout/FirstMethodArgumentLineBreak:
|
||||
Enabled: true
|
||||
Layout/HashAlignment:
|
||||
EnforcedHashRocketStyle: table
|
||||
EnforcedColonStyle: table
|
||||
EnforcedLastArgumentHashStyle: always_inspect
|
||||
Layout/MultilineHashKeyLineBreaks:
|
||||
Enabled: true
|
||||
Layout/MultilineMethodArgumentLineBreaks:
|
||||
Enabled: true
|
||||
@@ -0,0 +1 @@
|
||||
2.7.1
|
||||
@@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
source "https://rubygems.org"
|
||||
|
||||
git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
|
||||
|
||||
gem "require_all", "~> 3.0.0"
|
||||
|
||||
group :development do
|
||||
gem "dotenv", "~> 2.7.6"
|
||||
gem "factory_bot", "~> 6.1"
|
||||
gem "faker", "~> 2.17"
|
||||
gem "pry-byebug", "~> 3.9"
|
||||
gem "rspec", "~> 3.10"
|
||||
gem "rspec-github", "~> 2.3", ">= 2.3.1"
|
||||
gem "rubocop", "~> 0.80", "< 0.81"
|
||||
gem "rubocop-github", "~> 0.14.0"
|
||||
gem "rubocop-performance", "~> 1.6.1"
|
||||
gem "ruby-debug-ide", "~> 0.7.2"
|
||||
gem "shoulda-matchers", "~> 4.5.1"
|
||||
end
|
||||
@@ -0,0 +1,94 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
activesupport (6.1.4.1)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 1.6, < 2)
|
||||
minitest (>= 5.1)
|
||||
tzinfo (~> 2.0)
|
||||
zeitwerk (~> 2.3)
|
||||
ast (2.4.2)
|
||||
byebug (11.1.3)
|
||||
coderay (1.1.3)
|
||||
concurrent-ruby (1.1.9)
|
||||
diff-lcs (1.4.4)
|
||||
dotenv (2.7.6)
|
||||
factory_bot (6.2.0)
|
||||
activesupport (>= 5.0.0)
|
||||
faker (2.19.0)
|
||||
i18n (>= 1.6, < 2)
|
||||
i18n (1.8.11)
|
||||
concurrent-ruby (~> 1.0)
|
||||
jaro_winkler (1.5.4)
|
||||
method_source (1.0.0)
|
||||
minitest (5.14.4)
|
||||
parallel (1.21.0)
|
||||
parser (3.0.3.1)
|
||||
ast (~> 2.4.1)
|
||||
pry (0.13.1)
|
||||
coderay (~> 1.1)
|
||||
method_source (~> 1.0)
|
||||
pry-byebug (3.9.0)
|
||||
byebug (~> 11.0)
|
||||
pry (~> 0.13.0)
|
||||
rainbow (3.0.0)
|
||||
rake (13.0.6)
|
||||
require_all (3.0.0)
|
||||
rexml (3.2.5)
|
||||
rspec (3.10.0)
|
||||
rspec-core (~> 3.10.0)
|
||||
rspec-expectations (~> 3.10.0)
|
||||
rspec-mocks (~> 3.10.0)
|
||||
rspec-core (3.10.1)
|
||||
rspec-support (~> 3.10.0)
|
||||
rspec-expectations (3.10.1)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.10.0)
|
||||
rspec-github (2.3.1)
|
||||
rspec-core (~> 3.0)
|
||||
rspec-mocks (3.10.2)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.10.0)
|
||||
rspec-support (3.10.3)
|
||||
rubocop (0.80.1)
|
||||
jaro_winkler (~> 1.5.1)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 2.7.0.1)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
rexml
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 1.4.0, < 1.7)
|
||||
rubocop-github (0.14.0)
|
||||
rubocop (~> 0.59)
|
||||
rubocop-performance (1.6.1)
|
||||
rubocop (>= 0.71.0)
|
||||
ruby-debug-ide (0.7.3)
|
||||
rake (>= 0.8.1)
|
||||
ruby-progressbar (1.11.0)
|
||||
shoulda-matchers (4.5.1)
|
||||
activesupport (>= 4.2.0)
|
||||
tzinfo (2.0.4)
|
||||
concurrent-ruby (~> 1.0)
|
||||
unicode-display_width (1.6.1)
|
||||
zeitwerk (2.5.1)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
x86_64-darwin-19
|
||||
|
||||
DEPENDENCIES
|
||||
dotenv (~> 2.7.6)
|
||||
factory_bot (~> 6.1)
|
||||
faker (~> 2.17)
|
||||
pry-byebug (~> 3.9)
|
||||
require_all (~> 3.0.0)
|
||||
rspec (~> 3.10)
|
||||
rspec-github (~> 2.3, >= 2.3.1)
|
||||
rubocop (~> 0.80, < 0.81)
|
||||
rubocop-github (~> 0.14.0)
|
||||
rubocop-performance (~> 1.6.1)
|
||||
ruby-debug-ide (~> 0.7.2)
|
||||
shoulda-matchers (~> 4.5.1)
|
||||
|
||||
BUNDLED WITH
|
||||
2.2.33
|
||||
@@ -1 +1,82 @@
|
||||
# issue-ops
|
||||
# Valet Issue Ops
|
||||
|
||||
Valet can be orchestrated using GitHub Actions and Issues. This template repository can be used to enable this workflow to migrate pipelines from an existing CI/CD instance to GitHub Actions.
|
||||
|
||||
## Getting started
|
||||
|
||||
To get started create a new repository using this repository as the template by clicking [here](https://github.com/github/valet-issue-ops/generate). Next, add the repository secrets relevant to the CI/CD providers being migrated:
|
||||
|
||||
### All CI/CD providers
|
||||
|
||||
The following secrets are required:
|
||||
|
||||
- `VALET_GHCR_USERNAME`: The username to access the `valet` container.
|
||||
- `VALET_GHCR_PASSWORD`: The password to access the `valet` container (requires `read:packages` scope).
|
||||
- `GH_ACCESS_TOKEN`: GitHub personal access token to create pull requests (requires `repo` and `workflow` scopes).
|
||||
|
||||
Optionally, the following environment variables can be set:
|
||||
|
||||
- `GITHUB_INSTANCE_URL`: The base URL of your GitHub instance (only required if it is **not** <https://github.com>).
|
||||
|
||||
### Azure DevOps
|
||||
|
||||
The following secrets are required:
|
||||
|
||||
- `AZURE_DEVOPS_ACCESS_TOKEN`: The personal access token to access the Azure DevOps instance. This token requires the following scopes:
|
||||
- Build (Read & Execute)
|
||||
- Code (Read)
|
||||
- Release (Read)
|
||||
- Service Connections (Read)
|
||||
- Variable Groups (Read)
|
||||
|
||||
Optionally, the following environment variables can be set:
|
||||
|
||||
- `AZURE_DEVOPS_INSTANCE_URL`: The base URL of your Azure DevOps instance (only required if it is **not** <https://dev.azure.com>).
|
||||
|
||||
### Circle CI
|
||||
|
||||
The following secrets are required:
|
||||
|
||||
- `CIRCLE_CI_ACCESS_TOKEN`: The personal access token to access the Circle CI instance.
|
||||
- `CIRCLE_CI_SOURCE_GITHUB_ACCESS_TOKEN`: The personal access token to access pipeline files stored in GitHub.
|
||||
|
||||
Optionally, the following environment variables can be set:
|
||||
|
||||
- `CIRCLE_CI_INSTANCE_URL`: The base URL of your Circle CI instance (only required if it is **not** <https://circleci.com>).
|
||||
|
||||
### GitLab CI
|
||||
|
||||
The following secrets are required:
|
||||
|
||||
- `GITLAB_ACCESS_TOKEN`: The personal access token to access the GitLab CI instance (requires `read_api` scope).
|
||||
|
||||
Optionally, the following environment variables can be set:
|
||||
|
||||
- `GITLAB_INSTANCE_URL`: The base URL of your GitLab CI instance (only required if it is **not** <https://gitlab.com>).
|
||||
|
||||
### Jenkins
|
||||
|
||||
The following secrets are required:
|
||||
|
||||
- `JENKINSFILE_ACCESS_TOKEN`: The personal access token used to retrieve the contents of a `Jenkinsfile` stored in the build repository (requires `repo` scope).
|
||||
- `JENKINS_ACCESS_TOKEN`: The access token used to view Jenkins resources.
|
||||
- `JENKINS_USERNAME`: The username of the user's access token.
|
||||
|
||||
The following environment variables are required:
|
||||
|
||||
- `JENKINS_INSTANCE_URL`: The base URL of your Jenkins instance.
|
||||
|
||||
### Travis CI
|
||||
|
||||
The following secrets are required:
|
||||
|
||||
- `TRAVIS_CI_ACCESS_TOKEN`: The personal access token to access the Travis CI instance.
|
||||
- `TRAVIS_CI_SOURCE_GITHUB_ACCESS_TOKEN`: The personal access token to access pipeline files stored in GitHub.
|
||||
|
||||
Optionally, the following environment variables can be set:
|
||||
|
||||
- `TRAVIS_CI_INSTANCE_URL`: The base URL of your Travis CI instance (only required if it is **not** <https://travis-ci.com>).
|
||||
|
||||
## Pipeline migration
|
||||
|
||||
Once configured, pipelines can be migrated to GitHub Actions by opening an issue with the relevant issue template and following the instructions.
|
||||
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "bundler/setup"
|
||||
require "./cli"
|
||||
|
||||
Pry.start
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "yaml"
|
||||
require "open3"
|
||||
|
||||
require_relative "./../cli"
|
||||
|
||||
issue_content, comment_body, labels = ARGV
|
||||
|
||||
puts labels
|
||||
|
||||
raise "No issue content provided" if issue_content.nil?
|
||||
raise "No comment provided" if comment_body.nil?
|
||||
raise "No labels provided" if labels.nil?
|
||||
|
||||
provider = Provider.new(labels)
|
||||
command = Command.new(comment_body)
|
||||
|
||||
arguments = Arguments.new(provider, command, issue_content)
|
||||
|
||||
return unless command.valid?
|
||||
|
||||
provider.to_output
|
||||
command.to_output
|
||||
arguments.to_output
|
||||
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# This file was generated by Bundler.
|
||||
#
|
||||
# The application 'rspec' is installed as part of a gem, and
|
||||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require "pathname"
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path(
|
||||
"../../Gemfile",
|
||||
Pathname.new(__FILE__).realpath
|
||||
)
|
||||
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if /This file was generated by Bundler/.match?(File.read(bundle_binstub, 300))
|
||||
load(bundle_binstub)
|
||||
else
|
||||
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
||||
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
||||
end
|
||||
end
|
||||
|
||||
require "rubygems"
|
||||
require "bundler/setup"
|
||||
|
||||
load Gem.bin_path("rspec-core", "rspec")
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# This file was generated by Bundler.
|
||||
#
|
||||
# The application 'rubocop' is installed as part of a gem, and
|
||||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require "pathname"
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path(
|
||||
"../../Gemfile",
|
||||
Pathname.new(__FILE__).realpath
|
||||
)
|
||||
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if /This file was generated by Bundler/.match?(File.read(bundle_binstub, 300))
|
||||
load(bundle_binstub)
|
||||
else
|
||||
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
||||
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
||||
end
|
||||
end
|
||||
|
||||
require "rubygems"
|
||||
require "bundler/setup"
|
||||
|
||||
load Gem.bin_path("rubocop", "rubocop")
|
||||
@@ -0,0 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "require_all"
|
||||
require "pry" if ENV["VALET_CONTAINER"].nil?
|
||||
require "json"
|
||||
|
||||
require_all "lib"
|
||||
@@ -0,0 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module IssueParser
|
||||
def parameter_from_issue(name, text)
|
||||
match = text.match(/#{name}: ([^\n]+)/)
|
||||
return if match.nil?
|
||||
|
||||
match[1].strip
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OutputWriter
|
||||
def set_output(name, value)
|
||||
return if value.nil?
|
||||
|
||||
puts "::set-output name=#{name}::#{value}"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,29 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "../concerns/output_writer"
|
||||
|
||||
class Arguments
|
||||
include OutputWriter
|
||||
|
||||
def initialize(provider, command, issue_content)
|
||||
@args = argument_class(provider, command, issue_content)
|
||||
end
|
||||
|
||||
def argument_class(provider, command, issue_content)
|
||||
provider.module.const_get(command.classify).new(issue_content, command)
|
||||
end
|
||||
|
||||
def to_output
|
||||
arguments = @args.to_a
|
||||
return if arguments.blank?
|
||||
|
||||
set_output(
|
||||
"args",
|
||||
arguments.map do |a|
|
||||
next a unless a.include?(" ")
|
||||
|
||||
a.inspect
|
||||
end.join(" ")
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module AzureDevops
|
||||
class Audit
|
||||
include IssueParser
|
||||
|
||||
def initialize(issue_content, _)
|
||||
@organization = parameter_from_issue("Organization", issue_content)
|
||||
@project = parameter_from_issue("Project", issue_content)
|
||||
end
|
||||
|
||||
def to_a
|
||||
args = []
|
||||
args.concat(["--azure-devops-organization", @organization]) unless @organization.nil?
|
||||
args.concat(["--azure-devops-project", @project]) unless @project.nil?
|
||||
|
||||
return args unless args.empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,24 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module AzureDevops
|
||||
class DryRun
|
||||
include IssueParser
|
||||
|
||||
def initialize(issue_content, command)
|
||||
@organization = parameter_from_issue("Organization", issue_content)
|
||||
@project = parameter_from_issue("Project", issue_content)
|
||||
|
||||
@pipeline_type = command.options.fetch("pipeline-type", "pipeline")
|
||||
@pipeline_id = command.options["pipeline-id"]
|
||||
end
|
||||
|
||||
def to_a
|
||||
args = [@pipeline_type]
|
||||
args.concat(["--azure-devops-organization", @organization]) unless @organization.nil?
|
||||
args.concat(["--azure-devops-project", @project]) unless @project.nil?
|
||||
args.concat(["--pipeline-id", @pipeline_id]) unless @pipeline_id.nil?
|
||||
|
||||
return args unless args.empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module AzureDevops
|
||||
class Migrate
|
||||
include IssueParser
|
||||
|
||||
def initialize(issue_content, command)
|
||||
@organization = parameter_from_issue("Organization", issue_content)
|
||||
@project = parameter_from_issue("Project", issue_content)
|
||||
|
||||
@pipeline_type = command.options.fetch("pipeline-type", "pipeline")
|
||||
@pipeline_id = command.options["pipeline-id"]
|
||||
@target_url = command.options["target-url"]
|
||||
end
|
||||
|
||||
def to_a
|
||||
args = [@pipeline_type]
|
||||
args.concat(["--azure-devops-organization", @organization]) unless @organization.nil?
|
||||
args.concat(["--azure-devops-project", @project]) unless @project.nil?
|
||||
args.concat(["--pipeline-id", @pipeline_id]) unless @pipeline_id.nil?
|
||||
args.concat(["--target-url", @target_url]) unless @target_url.nil?
|
||||
|
||||
return args unless args.empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module CircleCI
|
||||
class Audit
|
||||
include IssueParser
|
||||
|
||||
def initialize(issue_content, _)
|
||||
@organization = parameter_from_issue("Organization", issue_content)
|
||||
end
|
||||
|
||||
def to_a
|
||||
return if @organization.nil?
|
||||
|
||||
["--circle-ci-organization", @organization]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module CircleCI
|
||||
class DryRun
|
||||
include IssueParser
|
||||
|
||||
def initialize(issue_content, command)
|
||||
# TODO: manually test this
|
||||
@organization = parameter_from_issue("Organization", issue_content)
|
||||
@project = command.options["project"]
|
||||
end
|
||||
|
||||
def to_a
|
||||
args = []
|
||||
args.concat(["--circle-ci-organization", @organization]) unless @organization.nil?
|
||||
args.concat(["--circle-ci-project", @project]) unless @project.nil?
|
||||
|
||||
return args unless args.empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,22 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module CircleCI
|
||||
class Migrate
|
||||
include IssueParser
|
||||
|
||||
def initialize(issue_content, command)
|
||||
@organization = parameter_from_issue("Organization", issue_content)
|
||||
@project = command.options["project"]
|
||||
@target_url = command.options["target-url"]
|
||||
end
|
||||
|
||||
def to_a
|
||||
args = []
|
||||
args.concat(["--circle-ci-organization", @organization]) unless @organization.nil?
|
||||
args.concat(["--circle-ci-project", @project]) unless @project.nil?
|
||||
args.concat(["--target-url", @target_url]) unless @target_url.nil?
|
||||
|
||||
return args unless args.empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,50 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "active_support/core_ext/string"
|
||||
require_relative "../concerns/output_writer"
|
||||
|
||||
class Command
|
||||
include OutputWriter
|
||||
|
||||
VALID_COMMANDS = %w[audit migrate dry-run].freeze
|
||||
|
||||
def initialize(comment_body)
|
||||
@comment_body = comment_body
|
||||
end
|
||||
|
||||
def command
|
||||
@command ||= begin
|
||||
command = @comment_body&.match(%r{/([^\s\\]+)})
|
||||
command[1] unless command.nil? || !VALID_COMMANDS.include?(command[1])
|
||||
end
|
||||
end
|
||||
|
||||
def valid?
|
||||
!command.nil?
|
||||
end
|
||||
|
||||
def classify
|
||||
return unless valid?
|
||||
|
||||
command.tr("-", "_").classify
|
||||
end
|
||||
|
||||
def options
|
||||
return unless valid?
|
||||
|
||||
@options ||= begin
|
||||
command_text = "/#{command}"
|
||||
command_index = @comment_body.index(command_text)
|
||||
|
||||
options_text = @comment_body[command_index + command_text.length..-1]
|
||||
options_text.split(" ")
|
||||
.each_slice(2)
|
||||
.to_h
|
||||
.transform_keys { |key| key.delete_prefix("--") }
|
||||
end
|
||||
end
|
||||
|
||||
def to_output
|
||||
set_output("command", command)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module GitlabCI
|
||||
class Audit
|
||||
include IssueParser
|
||||
|
||||
def initialize(issue_content, _)
|
||||
@namespace = parameter_from_issue("Namespace", issue_content)
|
||||
end
|
||||
|
||||
def to_a
|
||||
return if @namespace.nil?
|
||||
|
||||
["--namespace", @namespace]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module GitlabCI
|
||||
class DryRun
|
||||
include IssueParser
|
||||
|
||||
def initialize(issue_content, command)
|
||||
@namespace = parameter_from_issue("Namespace", issue_content)
|
||||
@project = command.options["project"]
|
||||
end
|
||||
|
||||
def to_a
|
||||
args = []
|
||||
args.concat(["--namespace", @namespace]) unless @namespace.nil?
|
||||
args.concat(["--project", @project]) unless @project.nil?
|
||||
|
||||
return args unless args.empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,22 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module GitlabCI
|
||||
class Migrate
|
||||
include IssueParser
|
||||
|
||||
def initialize(issue_content, command)
|
||||
@namespace = parameter_from_issue("Namespace", issue_content)
|
||||
@project = command.options["project"]
|
||||
@target_url = command.options["target-url"]
|
||||
end
|
||||
|
||||
def to_a
|
||||
args = []
|
||||
args.concat(["--namespace", @namespace]) unless @namespace.nil?
|
||||
args.concat(["--project", @project]) unless @project.nil?
|
||||
args.concat(["--target-url", @target_url]) unless @target_url.nil?
|
||||
|
||||
return args unless args.empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Jenkins
|
||||
class Audit
|
||||
include IssueParser
|
||||
|
||||
def initialize(issue_content, _)
|
||||
@folders = parameter_from_issue("Folders", issue_content)
|
||||
end
|
||||
|
||||
def to_a
|
||||
return if @folders.nil?
|
||||
|
||||
["--folders", @folders]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Jenkins
|
||||
class DryRun
|
||||
include IssueParser
|
||||
|
||||
def initialize(_, command)
|
||||
@source_url = command.options["source-url"]
|
||||
end
|
||||
|
||||
def to_a
|
||||
return if @source_url.nil?
|
||||
|
||||
["--source-url", @source_url]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Jenkins
|
||||
class Migrate
|
||||
include IssueParser
|
||||
|
||||
def initialize(_, command)
|
||||
@source_url = command.options["source-url"]
|
||||
@target_url = command.options["target-url"]
|
||||
end
|
||||
|
||||
def to_a
|
||||
args = []
|
||||
args.concat(["--source-url", @source_url]) unless @source_url.nil?
|
||||
args.concat(["--target-url", @target_url]) unless @target_url.nil?
|
||||
|
||||
return args unless args.empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,41 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_rel "./azure_devops/**/*.rb"
|
||||
require_rel "./circle_ci/**/*.rb"
|
||||
require_rel "./gitlab_ci/**/*.rb"
|
||||
require_rel "./jenkins/**/*.rb"
|
||||
require_rel "./travis_ci/**/*.rb"
|
||||
|
||||
class Provider
|
||||
include OutputWriter
|
||||
|
||||
PROVIDER_MAP = {
|
||||
"azure-devops" => ::AzureDevops,
|
||||
"circle-ci" => ::CircleCI,
|
||||
"gitlab" => ::GitlabCI,
|
||||
"jenkins" => ::Jenkins,
|
||||
"travis-ci" => ::TravisCI
|
||||
}.freeze
|
||||
|
||||
def initialize(labels)
|
||||
labels = labels.tr("\n", "").delete_prefix("[").delete_suffix("]").split(",").map(&:strip)
|
||||
providers = labels.select { |label| PROVIDER_MAP.key?(label) }
|
||||
|
||||
raise "One provider must be selected" if providers.empty?
|
||||
raise "Only one provider can be selected" unless providers.one?
|
||||
|
||||
@provider = providers.first
|
||||
end
|
||||
|
||||
def cli_command
|
||||
@provider
|
||||
end
|
||||
|
||||
def module
|
||||
PROVIDER_MAP[@provider]
|
||||
end
|
||||
|
||||
def to_output
|
||||
set_output("provider", cli_command)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module TravisCI
|
||||
class Audit
|
||||
include IssueParser
|
||||
|
||||
def initialize(issue_content, _)
|
||||
@organization = parameter_from_issue("Organization", issue_content)
|
||||
end
|
||||
|
||||
def to_a
|
||||
return if @organization.nil?
|
||||
|
||||
["--travis-ci-organization", @organization]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module TravisCI
|
||||
class DryRun
|
||||
include IssueParser
|
||||
|
||||
def initialize(issue_content, command)
|
||||
@organization = parameter_from_issue("Organization", issue_content)
|
||||
@repository = command.options["repository"]
|
||||
end
|
||||
|
||||
def to_a
|
||||
args = []
|
||||
args.concat(["--travis-ci-organization", @organization]) unless @organization.nil?
|
||||
args.concat(["--travis-ci-repository", @repository]) unless @repository.nil?
|
||||
|
||||
return args unless args.empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,22 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module TravisCI
|
||||
class Migrate
|
||||
include IssueParser
|
||||
|
||||
def initialize(issue_content, command)
|
||||
@organization = parameter_from_issue("Organization", issue_content)
|
||||
@repository = command.options["repository"]
|
||||
@target_url = command.options["target-url"]
|
||||
end
|
||||
|
||||
def to_a
|
||||
args = []
|
||||
args.concat(["--travis-ci-organization", @organization]) unless @organization.nil?
|
||||
args.concat(["--travis-ci-repository", @repository]) unless @repository.nil?
|
||||
args.concat(["--target-url", @target_url]) unless @target_url.nil?
|
||||
|
||||
return args unless args.empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe OutputWriter do
|
||||
let(:test_class) do
|
||||
class TestClass
|
||||
include OutputWriter
|
||||
end
|
||||
|
||||
TestClass.new
|
||||
end
|
||||
|
||||
describe "#set_output" do
|
||||
let(:name) { "var_name" }
|
||||
let(:value) { "var_value" }
|
||||
|
||||
subject { test_class.set_output(name, value) }
|
||||
|
||||
it { expect { subject }.to output(/::set-output name=#{name}::#{value}/).to_stdout }
|
||||
|
||||
context "when value is nil" do
|
||||
let(:value) { nil }
|
||||
|
||||
it { expect { subject }.not_to output(/::set-output name=#{name}::#{value}/).to_stdout }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,67 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe Arguments do
|
||||
let(:arguments) { Arguments.new(provider, command, issue_content) }
|
||||
let(:provider) { instance_double(Provider) }
|
||||
let(:command) { instance_double(Command) }
|
||||
let(:issue_content) { "some issue content" }
|
||||
|
||||
describe "#argument_class" do
|
||||
subject { arguments.argument_class(provider, command, issue_content) }
|
||||
|
||||
context "when the command is found" do
|
||||
before do
|
||||
expect(provider).to receive(:module).and_return(::Jenkins).at_least(:once)
|
||||
expect(command).to receive(:classify).and_return("Audit").at_least(:once)
|
||||
end
|
||||
|
||||
it { is_expected.to be_a(::Jenkins::Audit) }
|
||||
end
|
||||
|
||||
context "when the command is not found" do
|
||||
before do
|
||||
expect(provider).to receive(:module).and_return(::Jenkins).at_least(:once)
|
||||
expect(command).to receive(:classify).and_return("Whoopsie").at_least(:once)
|
||||
end
|
||||
|
||||
it { expect { subject }.to raise_error(NameError) }
|
||||
end
|
||||
end
|
||||
|
||||
describe "#to_output" do
|
||||
subject { arguments.to_output }
|
||||
|
||||
before do
|
||||
expect(provider).to receive(:module).and_return(::AzureDevops).at_least(:once)
|
||||
expect(command).to receive(:classify).and_return("Audit").at_least(:once)
|
||||
expect_any_instance_of(::AzureDevops::Audit).to receive(:to_a).and_return(output)
|
||||
end
|
||||
|
||||
context "when the output is nil" do
|
||||
let(:output) { nil }
|
||||
|
||||
it "does not write any output variable" do
|
||||
expect(arguments).not_to receive(:set_output)
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context "when the output is not nil" do
|
||||
let(:output) { ["--option", "value"] }
|
||||
|
||||
it "writes an output variable" do
|
||||
expect(arguments).to receive(:set_output).with("args", "--option value")
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context "when the output contains a space" do
|
||||
let(:output) { ["--option", "some value"] }
|
||||
|
||||
it "writes an output variable" do
|
||||
expect(arguments).to receive(:set_output).with("args", "--option \"some value\"")
|
||||
subject
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,42 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe AzureDevops::Audit do
|
||||
let(:audit) { described_class.new(issue_content, nil) }
|
||||
|
||||
describe "#to_a" do
|
||||
subject { audit.to_a }
|
||||
|
||||
context "when issue_content contains no args" do
|
||||
let(:issue_content) do
|
||||
<<~ISSUE
|
||||
Organization:
|
||||
Project:
|
||||
ISSUE
|
||||
end
|
||||
|
||||
it { is_expected.to be_nil }
|
||||
end
|
||||
|
||||
context "when issue_content contains an organization" do
|
||||
let(:issue_content) do
|
||||
<<~ISSUE
|
||||
Organization: my-organization
|
||||
Project:
|
||||
ISSUE
|
||||
end
|
||||
|
||||
it { is_expected.to eq(["--azure-devops-organization", "my-organization"]) }
|
||||
end
|
||||
|
||||
context "when issue_content contains a project" do
|
||||
let(:issue_content) do
|
||||
<<~ISSUE
|
||||
Organization: my-organization
|
||||
Project: my-project
|
||||
ISSUE
|
||||
end
|
||||
|
||||
it { is_expected.to eq(["--azure-devops-organization", "my-organization", "--azure-devops-project", "my-project"]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,28 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe AzureDevops::DryRun do
|
||||
let(:dry_run) { described_class.new(issue_content, command) }
|
||||
let(:command) { Command.new(comment_body) }
|
||||
|
||||
describe "#to_a" do
|
||||
subject { dry_run.to_a }
|
||||
let(:issue_content) do
|
||||
<<~ISSUE
|
||||
Organization: my-organization
|
||||
Project: my-project
|
||||
ISSUE
|
||||
end
|
||||
|
||||
context "when the comment body does not contain a pipeline type" do
|
||||
let(:comment_body) { "/dry-run" }
|
||||
|
||||
it { is_expected.to eq(["pipeline", "--azure-devops-organization", "my-organization", "--azure-devops-project", "my-project"]) }
|
||||
end
|
||||
|
||||
context "when the comment body contains a pipeline id" do
|
||||
let(:comment_body) { "/dry-run --pipeline-id 42" }
|
||||
|
||||
it { is_expected.to eq(["pipeline", "--azure-devops-organization", "my-organization", "--azure-devops-project", "my-project", "--pipeline-id", "42"]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,28 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe AzureDevops::Migrate do
|
||||
let(:dry_run) { described_class.new(issue_content, command) }
|
||||
let(:command) { Command.new(comment_body) }
|
||||
|
||||
describe "#to_a" do
|
||||
subject { dry_run.to_a }
|
||||
let(:issue_content) do
|
||||
<<~ISSUE
|
||||
Organization: my-organization
|
||||
Project: my-project
|
||||
ISSUE
|
||||
end
|
||||
|
||||
context "when the comment body does not contain a pipeline type" do
|
||||
let(:comment_body) { "/migrate" }
|
||||
|
||||
it { is_expected.to eq(["pipeline", "--azure-devops-organization", "my-organization", "--azure-devops-project", "my-project"]) }
|
||||
end
|
||||
|
||||
context "when the comment body contains a pipeline id" do
|
||||
let(:comment_body) { "/migrate --pipeline-id 42 --target-url https://github.com/org/repo" }
|
||||
|
||||
it { is_expected.to eq(["pipeline", "--azure-devops-organization", "my-organization", "--azure-devops-project", "my-project", "--pipeline-id", "42", "--target-url", "https://github.com/org/repo"]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,29 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe CircleCI::Audit do
|
||||
let(:audit) { described_class.new(issue_content, nil) }
|
||||
|
||||
describe "#to_a" do
|
||||
subject { audit.to_a }
|
||||
|
||||
context "when issue_content contains no args" do
|
||||
let(:issue_content) do
|
||||
<<~ISSUE
|
||||
Organization:
|
||||
ISSUE
|
||||
end
|
||||
|
||||
it { is_expected.to be_nil }
|
||||
end
|
||||
|
||||
context "when issue_content contains a organization" do
|
||||
let(:issue_content) do
|
||||
<<~ISSUE
|
||||
Organization: testing
|
||||
ISSUE
|
||||
end
|
||||
|
||||
it { is_expected.to eq(["--circle-ci-organization", "testing"]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe CircleCI::DryRun do
|
||||
let(:dry_run) { described_class.new(issue_content, command) }
|
||||
let(:command) { Command.new(comment_body) }
|
||||
|
||||
describe "#to_a" do
|
||||
subject { dry_run.to_a }
|
||||
let(:issue_content) do
|
||||
<<~ISSUE
|
||||
Organization: testing
|
||||
ISSUE
|
||||
end
|
||||
|
||||
context "when the comment body contains a project" do
|
||||
let(:comment_body) { "/dry-run --project repo" }
|
||||
|
||||
it { is_expected.to eq(["--circle-ci-organization", "testing", "--circle-ci-project", "repo"]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe CircleCI::Migrate do
|
||||
let(:dry_run) { described_class.new(issue_content, command) }
|
||||
let(:command) { Command.new(comment_body) }
|
||||
|
||||
describe "#to_a" do
|
||||
subject { dry_run.to_a }
|
||||
let(:issue_content) do
|
||||
<<~ISSUE
|
||||
Organization: testing
|
||||
ISSUE
|
||||
end
|
||||
|
||||
context "when the comment body contains a project" do
|
||||
let(:comment_body) { "/migrate --project repo --target-url https://github.com/org/repo" }
|
||||
|
||||
it { is_expected.to eq(["--circle-ci-organization", "testing", "--circle-ci-project", "repo", "--target-url", "https://github.com/org/repo"]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,109 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe Command do
|
||||
let(:command) { described_class.new(comment_body) }
|
||||
|
||||
describe "#command" do
|
||||
subject { command.command }
|
||||
|
||||
context "when no command is present" do
|
||||
let(:comment_body) do
|
||||
<<~COMMENT
|
||||
This is just a comment.
|
||||
COMMENT
|
||||
end
|
||||
|
||||
it { is_expected.to be_nil }
|
||||
end
|
||||
|
||||
context "when an unsupported comment is present" do
|
||||
let(:comment_body) do
|
||||
<<~COMMENT
|
||||
/do-something-unsupported
|
||||
COMMENT
|
||||
end
|
||||
|
||||
it { is_expected.to be_nil }
|
||||
end
|
||||
|
||||
described_class::VALID_COMMANDS.each do |c|
|
||||
context "when a supported command is present" do
|
||||
let(:comment_body) do
|
||||
<<~COMMENT
|
||||
/#{c}
|
||||
COMMENT
|
||||
end
|
||||
|
||||
it { is_expected.to eq(c) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#options" do
|
||||
subject { command.options }
|
||||
|
||||
context "when the command is not valid" do
|
||||
let(:comment_body) { "/do-something-unsupported --option-one value" }
|
||||
|
||||
it { is_expected.to be_nil }
|
||||
end
|
||||
|
||||
context "when no options are present" do
|
||||
let(:comment_body) { "/dry-run" }
|
||||
|
||||
it { is_expected.to be_blank }
|
||||
end
|
||||
|
||||
context "when options are present" do
|
||||
let(:comment_body) { "/dry-run --option-one value-one --option-two value-two" }
|
||||
|
||||
it { is_expected.to eq({ "option-one" => "value-one", "option-two" => "value-two" }) }
|
||||
end
|
||||
end
|
||||
|
||||
describe "#valid?" do
|
||||
subject { command.valid? }
|
||||
|
||||
context "when the command is supported" do
|
||||
let(:comment_body) { "/dry-run" }
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
|
||||
context "when the command is not supported" do
|
||||
let(:comment_body) { "/run-something-else" }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
|
||||
describe "#classify" do
|
||||
subject { command.classify }
|
||||
|
||||
[%w[audit Audit], %w[dry-run DryRun], %w[migrate Migrate]].each do |command, klass|
|
||||
context "when the command is #{command}" do
|
||||
let(:comment_body) { "/#{command}" }
|
||||
|
||||
it { is_expected.to eq(klass) }
|
||||
end
|
||||
end
|
||||
|
||||
context "when the command is invalid" do
|
||||
let(:comment_body) { "/do-something-unsupported" }
|
||||
|
||||
it { is_expected.to be_nil }
|
||||
end
|
||||
end
|
||||
|
||||
describe "to_output" do
|
||||
subject { command.to_output }
|
||||
|
||||
let(:comment_body) do
|
||||
<<~COMMENT
|
||||
/audit
|
||||
COMMENT
|
||||
end
|
||||
|
||||
it { expect { subject }.to output(/::set-output name=command::audit/).to_stdout }
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,29 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe GitlabCI::Audit do
|
||||
let(:audit) { described_class.new(issue_content, nil) }
|
||||
|
||||
describe "#to_a" do
|
||||
subject { audit.to_a }
|
||||
|
||||
context "when issue_content contains no args" do
|
||||
let(:issue_content) do
|
||||
<<~ISSUE
|
||||
Namespace:
|
||||
ISSUE
|
||||
end
|
||||
|
||||
it { is_expected.to be_nil }
|
||||
end
|
||||
|
||||
context "when issue_content contains a namespace" do
|
||||
let(:issue_content) do
|
||||
<<~ISSUE
|
||||
Namespace: testing
|
||||
ISSUE
|
||||
end
|
||||
|
||||
it { is_expected.to eq(["--namespace", "testing"]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe GitlabCI::DryRun do
|
||||
let(:dry_run) { described_class.new(issue_content, command) }
|
||||
let(:command) { Command.new(comment_body) }
|
||||
|
||||
describe "#to_a" do
|
||||
subject { dry_run.to_a }
|
||||
let(:issue_content) do
|
||||
<<~ISSUE
|
||||
Namespace: testing
|
||||
ISSUE
|
||||
end
|
||||
|
||||
context "when the comment body contains a pipeline id" do
|
||||
let(:comment_body) { "/dry-run --project project" }
|
||||
|
||||
it { is_expected.to eq(["--namespace", "testing", "--project", "project"]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe GitlabCI::Migrate do
|
||||
let(:dry_run) { described_class.new(issue_content, command) }
|
||||
let(:command) { Command.new(comment_body) }
|
||||
|
||||
describe "#to_a" do
|
||||
subject { dry_run.to_a }
|
||||
let(:issue_content) do
|
||||
<<~ISSUE
|
||||
Namespace: testing
|
||||
ISSUE
|
||||
end
|
||||
|
||||
context "when the comment body contains a pipeline id" do
|
||||
let(:comment_body) { "/migrate --project project --target-url https://github.com/org/repo" }
|
||||
|
||||
it { is_expected.to eq(["--namespace", "testing", "--project", "project", "--target-url", "https://github.com/org/repo"]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,29 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe Jenkins::Audit do
|
||||
let(:audit) { described_class.new(issue_content, nil) }
|
||||
|
||||
describe "#to_a" do
|
||||
subject { audit.to_a }
|
||||
|
||||
context "when issue_content contains no args" do
|
||||
let(:issue_content) do
|
||||
<<~ISSUE
|
||||
Folders:
|
||||
ISSUE
|
||||
end
|
||||
|
||||
it { is_expected.to be_nil }
|
||||
end
|
||||
|
||||
context "when issue_content contains a folder" do
|
||||
let(:issue_content) do
|
||||
<<~ISSUE
|
||||
Folders: test, prod
|
||||
ISSUE
|
||||
end
|
||||
|
||||
it { is_expected.to eq(["--folders", "test, prod"]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe Jenkins::DryRun do
|
||||
let(:dry_run) { described_class.new(nil, command) }
|
||||
let(:command) { Command.new(comment_body) }
|
||||
|
||||
describe "#to_a" do
|
||||
subject { dry_run.to_a }
|
||||
|
||||
context "when the comment body contains a pipeline id" do
|
||||
let(:comment_body) { "/dry-run --source-url https://jenkins.company.com/job/pipeline" }
|
||||
|
||||
it { is_expected.to eq(["--source-url", "https://jenkins.company.com/job/pipeline"]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe Jenkins::Migrate do
|
||||
let(:dry_run) { described_class.new(nil, command) }
|
||||
let(:command) { Command.new(comment_body) }
|
||||
|
||||
describe "#to_a" do
|
||||
subject { dry_run.to_a }
|
||||
|
||||
context "when the comment body contains a pipeline id" do
|
||||
let(:comment_body) { "/migrate --source-url https://jenkins.company.com/job/pipeline --target-url https://github.com/org/repo" }
|
||||
|
||||
it { is_expected.to eq(["--source-url", "https://jenkins.company.com/job/pipeline", "--target-url", "https://github.com/org/repo"]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,97 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe Provider do
|
||||
let(:provider) { described_class.new(labels) }
|
||||
|
||||
describe "#initialize" do
|
||||
subject { provider }
|
||||
|
||||
context "when no providers are selected" do
|
||||
let(:labels) do
|
||||
<<~LABELS
|
||||
[]
|
||||
LABELS
|
||||
end
|
||||
|
||||
it { expect { subject }.to raise_error("One provider must be selected") }
|
||||
end
|
||||
|
||||
context "when multiple providers are selected" do
|
||||
let(:labels) do
|
||||
<<~LABELS
|
||||
[
|
||||
jenkins,
|
||||
azure-devops
|
||||
]
|
||||
LABELS
|
||||
end
|
||||
|
||||
it { expect { subject }.to raise_error("Only one provider can be selected") }
|
||||
end
|
||||
|
||||
context "when an unsupported provider is selected" do
|
||||
let(:labels) do
|
||||
<<~LABELS
|
||||
[
|
||||
something-unsupported
|
||||
]
|
||||
LABELS
|
||||
end
|
||||
|
||||
it { expect { subject }.to raise_error("One provider must be selected") }
|
||||
end
|
||||
|
||||
context "when a single provider is selected" do
|
||||
let(:labels) do
|
||||
<<~LABELS
|
||||
[
|
||||
jenkins
|
||||
]
|
||||
LABELS
|
||||
end
|
||||
|
||||
it { expect { subject }.not_to raise_error }
|
||||
end
|
||||
end
|
||||
|
||||
describe "#cli_command" do
|
||||
subject { provider.cli_command }
|
||||
let(:labels) do
|
||||
<<~LABELS
|
||||
[
|
||||
gitlab
|
||||
]
|
||||
LABELS
|
||||
end
|
||||
|
||||
it { is_expected.to eq("gitlab") }
|
||||
end
|
||||
|
||||
describe "#module" do
|
||||
subject { provider.module }
|
||||
|
||||
let(:labels) do
|
||||
<<~LABELS
|
||||
[
|
||||
travis-ci
|
||||
]
|
||||
LABELS
|
||||
end
|
||||
|
||||
it { is_expected.to eq(::TravisCI) }
|
||||
end
|
||||
|
||||
describe "to_output" do
|
||||
subject { provider.to_output }
|
||||
|
||||
let(:labels) do
|
||||
<<~LABELS
|
||||
[
|
||||
azure-devops
|
||||
]
|
||||
LABELS
|
||||
end
|
||||
|
||||
it { expect { subject }.to output(/::set-output name=provider::azure-devops/).to_stdout }
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,29 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe TravisCI::Audit do
|
||||
let(:audit) { described_class.new(issue_content, nil) }
|
||||
|
||||
describe "#to_a" do
|
||||
subject { audit.to_a }
|
||||
|
||||
context "when issue_content contains no args" do
|
||||
let(:issue_content) do
|
||||
<<~ISSUE
|
||||
Organization:
|
||||
ISSUE
|
||||
end
|
||||
|
||||
it { is_expected.to be_nil }
|
||||
end
|
||||
|
||||
context "when issue_content contains an organization" do
|
||||
let(:issue_content) do
|
||||
<<~ISSUE
|
||||
Organization: testing
|
||||
ISSUE
|
||||
end
|
||||
|
||||
it { is_expected.to eq(["--travis-ci-organization", "testing"]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe TravisCI::DryRun do
|
||||
let(:dry_run) { described_class.new(issue_content, command) }
|
||||
let(:command) { Command.new(comment_body) }
|
||||
|
||||
describe "#to_a" do
|
||||
subject { dry_run.to_a }
|
||||
let(:issue_content) do
|
||||
<<~ISSUE
|
||||
Organization: testing
|
||||
ISSUE
|
||||
end
|
||||
|
||||
context "when the comment body contains a pipeline id" do
|
||||
let(:comment_body) { "/dry-run --repository repo" }
|
||||
|
||||
it { is_expected.to eq(["--travis-ci-organization", "testing", "--travis-ci-repository", "repo"]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe TravisCI::Migrate do
|
||||
let(:dry_run) { described_class.new(issue_content, command) }
|
||||
let(:command) { Command.new(comment_body) }
|
||||
|
||||
describe "#to_a" do
|
||||
subject { dry_run.to_a }
|
||||
let(:issue_content) do
|
||||
<<~ISSUE
|
||||
Organization: testing
|
||||
ISSUE
|
||||
end
|
||||
|
||||
context "when the comment body contains a pipeline id" do
|
||||
let(:comment_body) { "/migrate --repository repo --target-url https://github.com/org/repo" }
|
||||
|
||||
it { is_expected.to eq(["--travis-ci-organization", "testing", "--travis-ci-repository", "repo", "--target-url", "https://github.com/org/repo"]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,29 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "bundler/setup"
|
||||
require "factory_bot"
|
||||
require "faker"
|
||||
|
||||
require_relative "./../cli"
|
||||
|
||||
Dir[File.join(__dir__, "support/**/*.rb")].sort.each { |f| require f }
|
||||
|
||||
RSpec.configure do |config|
|
||||
# Enable flags like --only-failures and --next-failure
|
||||
config.example_status_persistence_file_path = ".rspec_status"
|
||||
|
||||
# Disable RSpec exposing methods globally on `Module` and `main`
|
||||
config.disable_monkey_patching!
|
||||
|
||||
config.expect_with :rspec do |c|
|
||||
# Override truncation of expected/got output
|
||||
c.max_formatted_output_length = 1000
|
||||
c.syntax = :expect
|
||||
end
|
||||
|
||||
config.include FactoryBot::Syntax::Methods
|
||||
|
||||
config.before(:suite) do
|
||||
FactoryBot.find_definitions
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "shoulda-matchers"
|
||||
|
||||
Shoulda::Matchers.configure do |config|
|
||||
config.integrate do |with|
|
||||
with.test_framework :rspec
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user