├── docs ├── CNAME ├── Gemfile ├── .gitignore ├── _includes │ ├── reference.md │ ├── example.html │ ├── google_analytics.html │ ├── js.html │ ├── content.html │ ├── edit-on-github.html │ ├── prev_next.md │ ├── tutorials.md │ ├── nav.html │ ├── head.html │ ├── subnav.html │ ├── footer.html │ └── commands.html ├── img │ ├── logos │ │ ├── boltops-logo.png │ │ ├── project-logo.png │ │ ├── boltops-logo-full.png │ │ └── pipedream-with-text.png │ └── docs │ │ ├── codepipeline-output.png │ │ └── multiple-codebuild-projects-pipeline.png ├── vendor │ ├── font-awesome │ │ ├── fonts │ │ │ ├── FontAwesome.otf │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.ttf │ │ │ ├── fontawesome-webfont.woff │ │ │ └── fontawesome-webfont.woff2 │ │ ├── less │ │ │ ├── screen-reader.less │ │ │ ├── fixed-width.less │ │ │ ├── larger.less │ │ │ ├── list.less │ │ │ ├── core.less │ │ │ ├── stacked.less │ │ │ ├── font-awesome.less │ │ │ ├── bordered-pulled.less │ │ │ ├── rotated-flipped.less │ │ │ ├── path.less │ │ │ ├── animated.less │ │ │ └── mixins.less │ │ └── scss │ │ │ ├── _fixed-width.scss │ │ │ ├── _screen-reader.scss │ │ │ ├── _larger.scss │ │ │ ├── _list.scss │ │ │ ├── _core.scss │ │ │ ├── font-awesome.scss │ │ │ ├── _stacked.scss │ │ │ ├── _bordered-pulled.scss │ │ │ ├── _rotated-flipped.scss │ │ │ ├── _path.scss │ │ │ ├── _animated.scss │ │ │ └── _mixins.scss │ ├── simple-line-icons │ │ └── fonts │ │ │ ├── Simple-Line-Icons.eot │ │ │ ├── Simple-Line-Icons.ttf │ │ │ ├── Simple-Line-Icons.woff │ │ │ └── Simple-Line-Icons.woff2 │ ├── jquery-easing │ │ ├── jquery.easing.compatibility.js │ │ ├── jquery.easing.min.js │ │ └── jquery.easing.js │ └── bootstrap │ │ └── css │ │ ├── bootstrap-reboot.min.css.map │ │ └── bootstrap-reboot.min.css ├── bin │ └── web ├── _layouts │ └── default.html ├── _reference │ ├── pipe-version.md │ ├── pipe-completion_script.md │ ├── pipe-delete.md │ ├── pipe-init.md │ ├── pipe-completion.md │ ├── pipe-start.md │ └── pipe-deploy.md ├── _sass │ ├── _table.scss │ ├── _download.scss │ ├── _variables.scss │ ├── _bootstrap-overrides.scss │ ├── _cta.scss │ ├── _footer.scss │ ├── _features.scss │ ├── _contact.scss │ ├── _global.scss │ ├── _mixins.scss │ ├── _masthead.scss │ ├── _navbar.scss │ ├── _syntax.scss │ └── _timeline.scss ├── reference.md ├── new-age.scss ├── support.md ├── _docs │ ├── next-steps.md │ ├── install.md │ ├── examples │ │ ├── codebuild-project.md │ │ ├── different-branches.md │ │ └── multiple-codebuild-projects.md │ ├── structure.md │ ├── dsl │ │ ├── schedule.md │ │ ├── webhook.md │ │ ├── pipeline │ │ │ ├── action.md │ │ │ ├── approve.md │ │ │ └── codebuild.md │ │ ├── sns.md │ │ ├── pipeline.md │ │ └── role.md │ ├── settings.md │ ├── dsl.md │ ├── start.md │ ├── conventions.md │ ├── ecs-deploy.md │ └── deploy.md ├── js │ ├── new-age.min.js │ ├── nav.js │ └── new-age.js ├── README.md ├── LICENSE ├── docs.md ├── index.html ├── _config.yml └── quick-start.md ├── .github └── FUNDING.yml ├── spec ├── fixtures │ ├── app │ │ └── .pipedream │ │ │ ├── schedule.rb │ │ │ ├── webhook.rb │ │ │ └── pipeline.rb │ └── pipelines │ │ ├── approve.rb │ │ └── approve_existing_sns.rb ├── lib │ ├── role_spec.rb │ ├── webhook_spec.rb │ ├── pipeline_spec.rb │ ├── schedule_spec.rb │ ├── cli_spec.rb │ └── pipeline │ │ └── approve_spec.rb └── spec_helper.rb ├── .rspec ├── lib ├── pipedream │ ├── version.rb │ ├── help │ │ ├── completion_script.md │ │ ├── completion.md │ │ ├── start.md │ │ └── deploy.md │ ├── completer │ │ ├── script.rb │ │ └── script.sh │ ├── build.rb │ ├── help.rb │ ├── dsl │ │ ├── sns.rb │ │ ├── schedule.rb │ │ ├── ssm.rb │ │ ├── webhook.rb │ │ ├── pipeline.rb │ │ ├── pipeline │ │ │ ├── approve.rb │ │ │ ├── codebuild.rb │ │ │ └── github.rb │ │ └── role.rb │ ├── create.rb │ ├── update.rb │ ├── aws_services.rb │ ├── autoloader.rb │ ├── delete.rb │ ├── deploy.rb │ ├── sns.rb │ ├── evaluate.rb │ ├── pipeline.rb │ ├── webhook.rb │ ├── sequence.rb │ ├── core.rb │ ├── cli.rb │ ├── init.rb │ ├── pipeline │ │ └── s3_bucket.rb │ ├── command.rb │ ├── aws_services │ │ └── helpers.rb │ ├── setting.rb │ ├── schedule.rb │ ├── start.rb │ └── stack.rb ├── template │ └── .pipedream │ │ ├── schedule.rb │ │ ├── settings.yml │ │ ├── sns.rb │ │ ├── README.md │ │ └── pipeline.rb.tt └── pipedream.rb ├── .cody ├── project.rb └── buildspec.yml ├── Gemfile ├── .gitignore ├── exe ├── pipe └── pipedream ├── .gitmodules ├── Rakefile ├── Guardfile ├── LICENSE.txt ├── CHANGELOG.md ├── pipedream.gemspec └── README.md /docs/CNAME: -------------------------------------------------------------------------------- 1 | pipedream.run 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: boltops-tools 2 | -------------------------------------------------------------------------------- /spec/fixtures/app/.pipedream/schedule.rb: -------------------------------------------------------------------------------- 1 | rate "1 day" -------------------------------------------------------------------------------- /spec/fixtures/app/.pipedream/webhook.rb: -------------------------------------------------------------------------------- 1 | github_token("fake") -------------------------------------------------------------------------------- /docs/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "jekyll" 4 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format documentation 3 | --require spec_helper 4 | -------------------------------------------------------------------------------- /lib/pipedream/version.rb: -------------------------------------------------------------------------------- 1 | module Pipedream 2 | VERSION = "0.4.8" 3 | end 4 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | _site 2 | .sass-cache 3 | .jekyll-metadata 4 | Gemfile.lock 5 | -------------------------------------------------------------------------------- /docs/_includes/reference.md: -------------------------------------------------------------------------------- 1 | Pipe Dream provides a DSL to make it easy create a CodePipeline pipeline. 2 | -------------------------------------------------------------------------------- /docs/img/logos/boltops-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boltops-tools/pipedream/HEAD/docs/img/logos/boltops-logo.png -------------------------------------------------------------------------------- /docs/img/logos/project-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boltops-tools/pipedream/HEAD/docs/img/logos/project-logo.png -------------------------------------------------------------------------------- /lib/template/.pipedream/schedule.rb: -------------------------------------------------------------------------------- 1 | # rate "1 day" 2 | # or 3 | # cron("0 10 * * ? *") # Run at 10:00 am (UTC) every day -------------------------------------------------------------------------------- /docs/img/docs/codepipeline-output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boltops-tools/pipedream/HEAD/docs/img/docs/codepipeline-output.png -------------------------------------------------------------------------------- /docs/img/logos/boltops-logo-full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boltops-tools/pipedream/HEAD/docs/img/logos/boltops-logo-full.png -------------------------------------------------------------------------------- /docs/img/logos/pipedream-with-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boltops-tools/pipedream/HEAD/docs/img/logos/pipedream-with-text.png -------------------------------------------------------------------------------- /docs/vendor/font-awesome/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boltops-tools/pipedream/HEAD/docs/vendor/font-awesome/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /lib/pipedream/help/completion_script.md: -------------------------------------------------------------------------------- 1 | To use, add the following to your `~/.bashrc` or `~/.profile` 2 | 3 | eval $(pipe completion_script) 4 | -------------------------------------------------------------------------------- /docs/img/docs/multiple-codebuild-projects-pipeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boltops-tools/pipedream/HEAD/docs/img/docs/multiple-codebuild-projects-pipeline.png -------------------------------------------------------------------------------- /docs/vendor/font-awesome/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boltops-tools/pipedream/HEAD/docs/vendor/font-awesome/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/vendor/font-awesome/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boltops-tools/pipedream/HEAD/docs/vendor/font-awesome/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /.cody/project.rb: -------------------------------------------------------------------------------- 1 | github_url("https://github.com/tongueroo/pipedream") 2 | linux_image("aws/codebuild/ruby:2.5.3-1.7.0") 3 | triggers(webhook: true) 4 | local_cache(false) -------------------------------------------------------------------------------- /docs/vendor/font-awesome/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boltops-tools/pipedream/HEAD/docs/vendor/font-awesome/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /docs/vendor/font-awesome/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boltops-tools/pipedream/HEAD/docs/vendor/font-awesome/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /docs/vendor/simple-line-icons/fonts/Simple-Line-Icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boltops-tools/pipedream/HEAD/docs/vendor/simple-line-icons/fonts/Simple-Line-Icons.eot -------------------------------------------------------------------------------- /docs/vendor/simple-line-icons/fonts/Simple-Line-Icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boltops-tools/pipedream/HEAD/docs/vendor/simple-line-icons/fonts/Simple-Line-Icons.ttf -------------------------------------------------------------------------------- /docs/vendor/simple-line-icons/fonts/Simple-Line-Icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boltops-tools/pipedream/HEAD/docs/vendor/simple-line-icons/fonts/Simple-Line-Icons.woff -------------------------------------------------------------------------------- /docs/vendor/simple-line-icons/fonts/Simple-Line-Icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boltops-tools/pipedream/HEAD/docs/vendor/simple-line-icons/fonts/Simple-Line-Icons.woff2 -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | # Specify your gem dependencies in codepipe.gemspec 4 | gemspec 5 | 6 | gem "codeclimate-test-reporter", group: :test, require: nil 7 | -------------------------------------------------------------------------------- /docs/bin/web: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ex 2 | 3 | # Usage: 4 | # bin/web 5 | # bin/web -P 8888 6 | 7 | bundle exec jekyll clean 8 | exec bundle exec jekyll serve --host 0.0.0.0 --trace "$@" 9 | -------------------------------------------------------------------------------- /docs/vendor/font-awesome/less/screen-reader.less: -------------------------------------------------------------------------------- 1 | // Screen Readers 2 | // ------------------------- 3 | 4 | .sr-only { .sr-only(); } 5 | .sr-only-focusable { .sr-only-focusable(); } 6 | -------------------------------------------------------------------------------- /docs/vendor/font-awesome/less/fixed-width.less: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .@{fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /docs/vendor/font-awesome/scss/_fixed-width.scss: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .#{$fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /docs/vendor/font-awesome/scss/_screen-reader.scss: -------------------------------------------------------------------------------- 1 | // Screen Readers 2 | // ------------------------- 3 | 4 | .sr-only { @include sr-only(); } 5 | .sr-only-focusable { @include sr-only-focusable(); } 6 | -------------------------------------------------------------------------------- /lib/pipedream/completer/script.rb: -------------------------------------------------------------------------------- 1 | class Pipedream::Completer::Script 2 | def self.generate 3 | bash_script = File.expand_path("script.sh", File.dirname(__FILE__)) 4 | puts "source #{bash_script}" 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /lib/template/.pipedream/settings.yml: -------------------------------------------------------------------------------- 1 | base: 2 | # stack_naming: 3 | # append_env: true # default false 4 | 5 | development: 6 | # aws_profile: dev_profile 7 | 8 | production: 9 | # aws_profile: prod_profile 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | _yardoc 7 | coverage 8 | doc/ 9 | InstalledFiles 10 | lib/bundler/man 11 | pkg 12 | rdoc 13 | spec/reports 14 | test/tmp 15 | test/version_tmp 16 | tmp 17 | Gemfile.lock 18 | -------------------------------------------------------------------------------- /lib/pipedream/build.rb: -------------------------------------------------------------------------------- 1 | module Pipedream 2 | class Build 3 | def initialize(options) 4 | @options = options 5 | end 6 | 7 | def run 8 | options = @options 9 | Pipeline.new(options).run 10 | end 11 | end 12 | end -------------------------------------------------------------------------------- /.cody/buildspec.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | 3 | phases: 4 | build: 5 | commands: 6 | - echo Build started on `date` 7 | - sed -i '/BUNDLED WITH/Q' Gemfile.lock # hack to fix bundler issue: allow different versions of bundler to work 8 | - bundle 9 | - bundle exec rspec 10 | -------------------------------------------------------------------------------- /docs/_layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% include head.html %} 4 | 5 | {% include nav.html %} 6 | 7 | {% include content.html %} 8 | 9 | {% include footer.html %} 10 | {% include js.html %} 11 | 12 | 13 | -------------------------------------------------------------------------------- /lib/pipedream/help.rb: -------------------------------------------------------------------------------- 1 | module Pipedream::Help 2 | class << self 3 | def text(namespaced_command) 4 | path = namespaced_command.to_s.gsub(':','/') 5 | path = File.expand_path("../help/#{path}.md", __FILE__) 6 | IO.read(path) if File.exist?(path) 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /exe/pipe: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # Trap ^C 4 | Signal.trap("INT") { 5 | puts "\nCtrl-C detected. Exiting..." 6 | sleep 0.1 7 | exit 8 | } 9 | 10 | $:.unshift(File.expand_path("../../lib", __FILE__)) 11 | require "pipedream" 12 | require "pipedream/cli" 13 | 14 | Pipedream::CLI.start(ARGV) 15 | -------------------------------------------------------------------------------- /exe/pipedream: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # Trap ^C 4 | Signal.trap("INT") { 5 | puts "\nCtrl-C detected. Exiting..." 6 | sleep 0.1 7 | exit 8 | } 9 | 10 | $:.unshift(File.expand_path("../../lib", __FILE__)) 11 | require "pipedream" 12 | require "pipedream/cli" 13 | 14 | Pipedream::CLI.start(ARGV) 15 | -------------------------------------------------------------------------------- /docs/_reference/pipe-version.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: pipe version 3 | reference: true 4 | --- 5 | 6 | ## Usage 7 | 8 | pipe version 9 | 10 | ## Description 11 | 12 | prints version 13 | 14 | 15 | ## Options 16 | 17 | ``` 18 | [--verbose], [--no-verbose] 19 | [--noop], [--no-noop] 20 | ``` 21 | 22 | -------------------------------------------------------------------------------- /lib/pipedream/completer/script.sh: -------------------------------------------------------------------------------- 1 | _codepipe() { 2 | COMPREPLY=() 3 | local word="${COMP_WORDS[COMP_CWORD]}" 4 | local words=("${COMP_WORDS[@]}") 5 | unset words[0] 6 | local completion=$(codepipe completion ${words[@]}) 7 | COMPREPLY=( $(compgen -W "$completion" -- "$word") ) 8 | } 9 | 10 | complete -F _codepipe codepipe 11 | -------------------------------------------------------------------------------- /lib/pipedream/dsl/sns.rb: -------------------------------------------------------------------------------- 1 | module Pipedream::Dsl 2 | module Sns 3 | PROPERTIES = %w[ 4 | display_name 5 | kms_master_key_id 6 | subscription 7 | topic_name 8 | ] 9 | PROPERTIES.each do |prop| 10 | define_method(prop) do |v| 11 | @properties[prop.to_sym] = v 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/cfn-status"] 2 | path = vendor/cfn-status 3 | url = https://github.com/tongueroo/cfn-status 4 | [submodule "vendor/cfn_camelizer"] 5 | path = vendor/cfn_camelizer 6 | url = https://github.com/tongueroo/cfn_camelizer 7 | [submodule "vendor/aws_data"] 8 | path = vendor/aws_data 9 | url = https://github.com/tongueroo/aws_data 10 | -------------------------------------------------------------------------------- /lib/pipedream/create.rb: -------------------------------------------------------------------------------- 1 | module Pipedream 2 | class Create < Stack 3 | def perform 4 | cfn.create_stack( 5 | stack_name: @stack_name, 6 | template_body: YAML.dump(@template), 7 | capabilities: ["CAPABILITY_IAM"] 8 | ) 9 | puts "Creating stack #{@stack_name}. Check CloudFormation console for status." 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/pipedream/update.rb: -------------------------------------------------------------------------------- 1 | module Pipedream 2 | class Update < Stack 3 | def perform 4 | cfn.update_stack( 5 | stack_name: @stack_name, 6 | template_body: YAML.dump(@template), 7 | capabilities: ["CAPABILITY_IAM"] 8 | ) 9 | puts "Updating stack #{@stack_name}. Check CloudFormation console for status." 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/fixtures/app/.pipedream/pipeline.rb: -------------------------------------------------------------------------------- 1 | stage "Source" do 2 | github( 3 | source: "tongueroo/demo-cb", 4 | branch: "master", 5 | auth_token: "fake", 6 | ) 7 | end 8 | stage "DeployStacks" do 9 | codebuild "demo1" # action declaration 10 | codebuild "demo2", "demo3" # will run in parallel. run_order=2 11 | codebuild "demo4" # action declaration 12 | end 13 | -------------------------------------------------------------------------------- /spec/lib/role_spec.rb: -------------------------------------------------------------------------------- 1 | describe Pipedream::Role do 2 | let(:role) do 3 | Pipedream::Role.new(role_path: "spec/fixtures/app/.pipedream/role.rb") 4 | end 5 | context "general" do 6 | it "builds up the template in memory" do 7 | template = role.run 8 | expect(template.keys).to eq ["IamRole"] 9 | expect(template["IamRole"]).to be_a(Hash) 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rspec/core/rake_task" 3 | 4 | task default: :spec 5 | 6 | RSpec::Core::RakeTask.new 7 | 8 | require_relative "lib/pipedream" 9 | require "cli_markdown" 10 | desc "Generates cli reference docs as markdown" 11 | task :docs do 12 | mkdir_p "docs/_includes" 13 | CliMarkdown::Creator.create_all(cli_class: Pipedream::CLI, cli_name: "pipe") 14 | end 15 | -------------------------------------------------------------------------------- /spec/lib/webhook_spec.rb: -------------------------------------------------------------------------------- 1 | describe Pipedream::Webhook do 2 | let(:webhook) do 3 | Pipedream::Webhook.new(webhook_path: "spec/fixtures/app/.pipedream/webhook.rb") 4 | end 5 | context "general" do 6 | it "builds up the template in memory" do 7 | template = webhook.run 8 | expect(template.keys).to eq ["Webhook"] 9 | expect(template["Webhook"]).to be_a(Hash) 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/lib/pipeline_spec.rb: -------------------------------------------------------------------------------- 1 | describe Pipedream::Pipeline do 2 | let(:pipeline) do 3 | Pipedream::Pipeline.new(pipeline_path: "spec/fixtures/app/.pipedream/pipeline.rb") 4 | end 5 | context "general" do 6 | it "builds up the template in memory" do 7 | template = pipeline.run 8 | expect(template.keys).to eq ["Pipeline"] 9 | expect(template["Pipeline"]).to be_a(Hash) 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/lib/schedule_spec.rb: -------------------------------------------------------------------------------- 1 | describe Pipedream::Schedule do 2 | let(:schedule) do 3 | Pipedream::Schedule.new(schedule_path: "spec/fixtures/app/.pipedream/schedule.rb") 4 | end 5 | context "general" do 6 | it "builds up the template in memory" do 7 | template = schedule.run 8 | expect(template.keys).to eq ["EventsRule", "EventsRuleRole"] 9 | expect(template["EventsRule"]).to be_a(Hash) 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /docs/vendor/font-awesome/less/larger.less: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .@{fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .@{fa-css-prefix}-2x { font-size: 2em; } 11 | .@{fa-css-prefix}-3x { font-size: 3em; } 12 | .@{fa-css-prefix}-4x { font-size: 4em; } 13 | .@{fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /docs/vendor/font-awesome/scss/_larger.scss: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .#{$fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .#{$fa-css-prefix}-2x { font-size: 2em; } 11 | .#{$fa-css-prefix}-3x { font-size: 3em; } 12 | .#{$fa-css-prefix}-4x { font-size: 4em; } 13 | .#{$fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /docs/_includes/example.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |

Demo Pipeline

6 |
7 |
8 |
9 | 10 |
11 |
12 |
13 | -------------------------------------------------------------------------------- /spec/lib/cli_spec.rb: -------------------------------------------------------------------------------- 1 | describe Pipedream::CLI do 2 | before(:all) do 3 | @args = "--noop" 4 | @old_root = Dir.pwd 5 | Dir.chdir("spec/fixtures/app") 6 | @pipe_bin = "../../../exe/pipe" 7 | end 8 | after(:all) do 9 | Dir.chdir(@old_root) 10 | end 11 | 12 | describe "pipe" do 13 | it "deploy" do 14 | out = execute("#{@pipe_bin} deploy #{@args}") 15 | expect(out).to include("Generated CloudFormation template") 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /docs/vendor/font-awesome/less/list.less: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: @fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .@{fa-css-prefix}-li { 11 | position: absolute; 12 | left: -@fa-li-width; 13 | width: @fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.@{fa-css-prefix}-lg { 17 | left: (-@fa-li-width + (4em / 14)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /docs/vendor/font-awesome/scss/_list.scss: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: $fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .#{$fa-css-prefix}-li { 11 | position: absolute; 12 | left: -$fa-li-width; 13 | width: $fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.#{$fa-css-prefix}-lg { 17 | left: -$fa-li-width + (4em / 14); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /spec/fixtures/pipelines/approve.rb: -------------------------------------------------------------------------------- 1 | stage "Source" do 2 | github( 3 | source: "tongueroo/demo-test", 4 | branch: "master", 5 | auth_token: "fake", 6 | ) 7 | end 8 | 9 | # codebuild_prefix "myprefix-" 10 | 11 | stage "Build" do 12 | codebuild "demo-build" 13 | end 14 | 15 | stage "Approve" do 16 | # not specifying the SNS topic arn so codepipeline will create one 17 | approve("Approve deployment") 18 | end 19 | 20 | stage "Deploy" do 21 | codebuild "demo-deploy" 22 | end 23 | -------------------------------------------------------------------------------- /docs/_reference/pipe-completion_script.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: pipe completion_script 3 | reference: true 4 | --- 5 | 6 | ## Usage 7 | 8 | pipe completion_script 9 | 10 | ## Description 11 | 12 | Generates a script that can be eval to setup auto-completion. 13 | 14 | To use, add the following to your `~/.bashrc` or `~/.profile` 15 | 16 | eval $(pipe completion_script) 17 | 18 | 19 | ## Options 20 | 21 | ``` 22 | [--verbose], [--no-verbose] 23 | [--noop], [--no-noop] 24 | ``` 25 | 26 | -------------------------------------------------------------------------------- /docs/_sass/_table.scss: -------------------------------------------------------------------------------- 1 | table { 2 | width: 100%; 3 | background-color: #ecf0f1; 4 | margin-bottom: 20px; 5 | } 6 | 7 | thead { 8 | } 9 | 10 | tr { 11 | 12 | } 13 | 14 | thead th { 15 | background-color: #000000; 16 | text-transform: uppercase; 17 | color: #cdcdce; 18 | } 19 | 20 | tbody { 21 | } 22 | 23 | tr { 24 | } 25 | 26 | tbody td { 27 | box-shadow: inset 0 1px 0 rgba(255,255,255,0.1); 28 | color: black; 29 | } 30 | 31 | th, 32 | td { 33 | padding: 10px; 34 | } 35 | -------------------------------------------------------------------------------- /lib/pipedream/aws_services.rb: -------------------------------------------------------------------------------- 1 | require "aws-sdk-codepipeline" 2 | require "aws-sdk-cloudformation" 3 | 4 | require "aws_mfa_secure/ext/aws" # add MFA support 5 | 6 | module Pipedream 7 | module AwsServices 8 | include Helpers 9 | 10 | def codepipeline 11 | @codepipeline ||= Aws::CodePipeline::Client.new 12 | end 13 | 14 | def cfn 15 | @cfn ||= Aws::CloudFormation::Client.new 16 | end 17 | 18 | def s3 19 | @s3 ||= Aws::S3::Client.new 20 | end 21 | end 22 | end -------------------------------------------------------------------------------- /lib/pipedream/help/completion.md: -------------------------------------------------------------------------------- 1 | Example: 2 | 3 | pipe completion 4 | 5 | Prints words for TAB auto-completion. 6 | 7 | Examples: 8 | 9 | pipe completion 10 | pipe completion hello 11 | pipe completion hello name 12 | 13 | To enable, TAB auto-completion add the following to your profile: 14 | 15 | eval $(pipe completion_script) 16 | 17 | Auto-completion example usage: 18 | 19 | pipe [TAB] 20 | pipe hello [TAB] 21 | pipe hello name [TAB] 22 | pipe hello name --[TAB] 23 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | guard "bundler", cmd: "bundle" do 2 | watch("Gemfile") 3 | watch(/^.+\.gemspec/) 4 | end 5 | 6 | guard :rspec, cmd: "bundle exec rspec" do 7 | require "guard/rspec/dsl" 8 | dsl = Guard::RSpec::Dsl.new(self) 9 | 10 | # RSpec files 11 | rspec = dsl.rspec 12 | watch(rspec.spec_helper) { rspec.spec_dir } 13 | watch(rspec.spec_support) { rspec.spec_dir } 14 | watch(rspec.spec_files) 15 | 16 | # Ruby files 17 | ruby = dsl.ruby 18 | dsl.watch_spec_files_for(ruby.lib_files) 19 | end 20 | -------------------------------------------------------------------------------- /docs/_includes/google_analytics.html: -------------------------------------------------------------------------------- 1 | 2 | 11 | -------------------------------------------------------------------------------- /docs/reference.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: CLI Reference 3 | --- 4 | {% include reference.md %} 5 | 6 | * [pipe completion]({% link _reference/pipe-completion.md %}) 7 | * [pipe completion_script]({% link _reference/pipe-completion_script.md %}) 8 | * [pipe delete]({% link _reference/pipe-delete.md %}) 9 | * [pipe deploy]({% link _reference/pipe-deploy.md %}) 10 | * [pipe init]({% link _reference/pipe-init.md %}) 11 | * [pipe start]({% link _reference/pipe-start.md %}) 12 | * [pipe version]({% link _reference/pipe-version.md %}) 13 | -------------------------------------------------------------------------------- /docs/new-age.scss: -------------------------------------------------------------------------------- 1 | --- 2 | # this ensures Jekyll reads the file to be transformed into CSS later 3 | # only Main files contain this front matter, not partials. 4 | --- 5 | 6 | @import "variables"; 7 | @import "mixins"; 8 | @import "global"; 9 | @import "navbar"; 10 | @import "masthead"; 11 | @import "download"; 12 | @import "features"; 13 | @import "cta"; 14 | @import "contact"; 15 | @import "footer"; 16 | @import "bootstrap-overrides"; 17 | @import "table"; 18 | @import "main"; 19 | @import "timeline"; 20 | @import "syntax"; 21 | -------------------------------------------------------------------------------- /docs/vendor/font-awesome/less/core.less: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix} { 5 | display: inline-block; 6 | font: normal normal normal @fa-font-size-base/@fa-line-height-base FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /docs/vendor/font-awesome/scss/_core.scss: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix} { 5 | display: inline-block; 6 | font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /spec/fixtures/pipelines/approve_existing_sns.rb: -------------------------------------------------------------------------------- 1 | stage "Source" do 2 | github( 3 | source: "tongueroo/demo-test", 4 | branch: "master", 5 | auth_token: "fake", 6 | ) 7 | end 8 | 9 | # codebuild_prefix "myprefix-" 10 | 11 | stage "Build" do 12 | codebuild "demo-build" 13 | end 14 | 15 | stage "Approve" do 16 | approve( 17 | notification_arn: "arn:aws:sns:us-west-2:536766270177:hello-topic", 18 | custom_data: "Approve deployment", 19 | ) 20 | end 21 | 22 | stage "Deploy" do 23 | codebuild "demo-deploy" 24 | end 25 | -------------------------------------------------------------------------------- /docs/vendor/font-awesome/scss/font-awesome.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables"; 7 | @import "mixins"; 8 | @import "path"; 9 | @import "core"; 10 | @import "larger"; 11 | @import "fixed-width"; 12 | @import "list"; 13 | @import "bordered-pulled"; 14 | @import "animated"; 15 | @import "rotated-flipped"; 16 | @import "stacked"; 17 | @import "icons"; 18 | @import "screen-reader"; 19 | -------------------------------------------------------------------------------- /lib/pipedream/autoloader.rb: -------------------------------------------------------------------------------- 1 | require "zeitwerk" 2 | 3 | module Pipedream 4 | class Autoloader 5 | class Inflector < Zeitwerk::Inflector 6 | def camelize(basename, _abspath) 7 | map = { cli: "CLI", version: "VERSION" } 8 | map[basename.to_sym] || super 9 | end 10 | end 11 | 12 | class << self 13 | def setup 14 | loader = Zeitwerk::Loader.new 15 | loader.inflector = Inflector.new 16 | loader.push_dir(File.dirname(__dir__)) # lib 17 | loader.setup 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /docs/vendor/font-awesome/less/stacked.less: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .@{fa-css-prefix}-stack-1x, .@{fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .@{fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .@{fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .@{fa-css-prefix}-inverse { color: @fa-inverse; } 21 | -------------------------------------------------------------------------------- /docs/_reference/pipe-delete.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: pipe delete 3 | reference: true 4 | --- 5 | 6 | ## Usage 7 | 8 | pipe delete 9 | 10 | ## Description 11 | 12 | Delete codebuild project. 13 | 14 | 15 | ## Options 16 | 17 | ``` 18 | [--sure=SURE] # Bypass are you sure prompt 19 | [--stack-name=STACK_NAME] # Override the generated stack name. If you use this you must always specify it 20 | [--wait], [--no-wait] # Wait for operation to complete 21 | # Default: true 22 | [--verbose], [--no-verbose] 23 | [--noop], [--no-noop] 24 | ``` 25 | 26 | -------------------------------------------------------------------------------- /docs/vendor/font-awesome/scss/_stacked.scss: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .#{$fa-css-prefix}-stack-1x, .#{$fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .#{$fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .#{$fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .#{$fa-css-prefix}-inverse { color: $fa-inverse; } 21 | -------------------------------------------------------------------------------- /docs/vendor/font-awesome/less/font-awesome.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables.less"; 7 | @import "mixins.less"; 8 | @import "path.less"; 9 | @import "core.less"; 10 | @import "larger.less"; 11 | @import "fixed-width.less"; 12 | @import "list.less"; 13 | @import "bordered-pulled.less"; 14 | @import "animated.less"; 15 | @import "rotated-flipped.less"; 16 | @import "stacked.less"; 17 | @import "icons.less"; 18 | @import "screen-reader.less"; 19 | -------------------------------------------------------------------------------- /docs/_includes/js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | {% if site.google_analytics and jekyll.environment == "production" %} 14 | {% include google_analytics.html %} 15 | {% endif %} 16 | -------------------------------------------------------------------------------- /docs/support.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Support 3 | nav_order: 22 4 | --- 5 | 6 | ## Getting Help 7 | 8 | If you're looking for support for Pipe Dream, here are some options: 9 | 10 | * Read the [Documentation](https://pipedream.run) 11 | 12 | ## Report a bug 13 | 14 | If you think you've found a bug within the pipedream repository, [open an issue](https://github.com/tongueroo/pipedream/issues/new/choose). 15 | 16 | Happy Hackin' 😁 17 | 18 | ## Commercial Support 19 | 20 | If you would like professional help, [BoltOps](https://www.boltops.com/) provides consulting. Feel free to reach out: [contact page](https://www.boltops.com/contact) 21 | 22 | {% include prev_next.md %} 23 | -------------------------------------------------------------------------------- /docs/_reference/pipe-init.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: pipe init 3 | reference: true 4 | --- 5 | 6 | ## Usage 7 | 8 | pipe init 9 | 10 | ## Description 11 | 12 | Set up initial .codepipline files. 13 | 14 | 15 | ## Options 16 | 17 | ``` 18 | [--name=NAME] # CodePipeline project name. 19 | [--mode=MODE] # Modes: light or full 20 | [--force] # Bypass overwrite are you sure prompt for existing files. 21 | [--template=TEMPLATE] # Custom template to use. 22 | [--template-mode=TEMPLATE_MODE] # Template mode: replace or additive. 23 | [--verbose], [--no-verbose] 24 | [--noop], [--no-noop] 25 | ``` 26 | 27 | -------------------------------------------------------------------------------- /lib/pipedream/dsl/schedule.rb: -------------------------------------------------------------------------------- 1 | module Pipedream::Dsl 2 | module Schedule 3 | PROPERTIES = %w[ 4 | description 5 | event_pattern 6 | name 7 | role_arn 8 | schedule_expression 9 | state 10 | targets 11 | ] 12 | PROPERTIES.each do |prop| 13 | define_method(prop) do |v| 14 | @properties[prop.to_sym] = v 15 | end 16 | end 17 | 18 | def rate(period) 19 | @schedule_expression = "rate(#{period})" 20 | end 21 | 22 | def cron(expression) 23 | @schedule_expression = "cron(#{expression})" 24 | end 25 | 26 | def rule_event(props={}) 27 | @rule_event_props = props 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/pipedream.rb: -------------------------------------------------------------------------------- 1 | $:.unshift(File.expand_path("../", __FILE__)) 2 | require "pipedream/version" 3 | require "rainbow/ext/string" 4 | require "memoist" 5 | require "active_support" 6 | require "active_support/core_ext/hash" 7 | 8 | require "pipedream/autoloader" 9 | Pipedream::Autoloader.setup 10 | 11 | gem_root = File.dirname(__dir__) 12 | $:.unshift("#{gem_root}/vendor/aws_data/lib") 13 | require "aws_data" 14 | $:.unshift("#{gem_root}/vendor/cfn_camelizer/lib") 15 | require "cfn_camelizer" 16 | $:.unshift("#{gem_root}/vendor/cfn-status/lib") 17 | require "cfn/status" 18 | 19 | module Pipedream 20 | class Error < StandardError; end 21 | extend Core 22 | end 23 | 24 | Pipedream.set_aws_profile! 25 | -------------------------------------------------------------------------------- /lib/template/.pipedream/sns.rb: -------------------------------------------------------------------------------- 1 | # If user has specified their own existing SNS topic ARN, then this SNS Topic managed by the codepipeline 2 | # tool will not get created. Also, only gets created if there's an approval action in the pipeline. 3 | # 4 | # Example properties: 5 | # 6 | # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sns-topic.html 7 | # 8 | # display_name "my display_name" 9 | # kms_master_key_id "String" 10 | # subscription([{ 11 | # endpoint: '', 12 | # protocol: ',' 13 | # }]) 14 | # topic_name "string", # Recommend not setting because update requires: Replacement. Allow CloudFormation to set it so 2 pipelines dont have same SNS Topic name that collides 15 | -------------------------------------------------------------------------------- /lib/template/.pipedream/README.md: -------------------------------------------------------------------------------- 1 | # Pipedream Files 2 | 3 | The files in folder are used by pipedream to build AWS CodePipeline pipelines. For more info, check out the [pipedream docs](https://pipedream.run). Here's a quick start. 4 | 5 | ## Install Tool 6 | 7 | gem install pipedream 8 | 9 | This installs both the `pipe` and `pipedream` commands. They do the same thing, the `pipe` command is just shorter to type. 10 | 11 | ## Update Project 12 | 13 | To update the CodePipeline pipelines: 14 | 15 | pipedream deploy demo 16 | 17 | ## Start a Execution 18 | 19 | To start a CodePipeline execution: 20 | 21 | pipedream start demo 22 | 23 | To specify a branch: 24 | 25 | pipedream start demo -b feature 26 | -------------------------------------------------------------------------------- /docs/vendor/font-awesome/less/bordered-pulled.less: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em @fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .@{fa-css-prefix}-pull-left { float: left; } 11 | .@{fa-css-prefix}-pull-right { float: right; } 12 | 13 | .@{fa-css-prefix} { 14 | &.@{fa-css-prefix}-pull-left { margin-right: .3em; } 15 | &.@{fa-css-prefix}-pull-right { margin-left: .3em; } 16 | } 17 | 18 | /* Deprecated as of 4.4.0 */ 19 | .pull-right { float: right; } 20 | .pull-left { float: left; } 21 | 22 | .@{fa-css-prefix} { 23 | &.pull-left { margin-right: .3em; } 24 | &.pull-right { margin-left: .3em; } 25 | } 26 | -------------------------------------------------------------------------------- /docs/vendor/font-awesome/less/rotated-flipped.less: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-rotate-90 { .fa-icon-rotate(90deg, 1); } 5 | .@{fa-css-prefix}-rotate-180 { .fa-icon-rotate(180deg, 2); } 6 | .@{fa-css-prefix}-rotate-270 { .fa-icon-rotate(270deg, 3); } 7 | 8 | .@{fa-css-prefix}-flip-horizontal { .fa-icon-flip(-1, 1, 0); } 9 | .@{fa-css-prefix}-flip-vertical { .fa-icon-flip(1, -1, 2); } 10 | 11 | // Hook for IE8-9 12 | // ------------------------- 13 | 14 | :root .@{fa-css-prefix}-rotate-90, 15 | :root .@{fa-css-prefix}-rotate-180, 16 | :root .@{fa-css-prefix}-rotate-270, 17 | :root .@{fa-css-prefix}-flip-horizontal, 18 | :root .@{fa-css-prefix}-flip-vertical { 19 | filter: none; 20 | } 21 | -------------------------------------------------------------------------------- /docs/vendor/font-awesome/scss/_bordered-pulled.scss: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em $fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .#{$fa-css-prefix}-pull-left { float: left; } 11 | .#{$fa-css-prefix}-pull-right { float: right; } 12 | 13 | .#{$fa-css-prefix} { 14 | &.#{$fa-css-prefix}-pull-left { margin-right: .3em; } 15 | &.#{$fa-css-prefix}-pull-right { margin-left: .3em; } 16 | } 17 | 18 | /* Deprecated as of 4.4.0 */ 19 | .pull-right { float: right; } 20 | .pull-left { float: left; } 21 | 22 | .#{$fa-css-prefix} { 23 | &.pull-left { margin-right: .3em; } 24 | &.pull-right { margin-left: .3em; } 25 | } 26 | -------------------------------------------------------------------------------- /lib/pipedream/dsl/ssm.rb: -------------------------------------------------------------------------------- 1 | require "aws-sdk-ssm" 2 | 3 | module Pipedream::Dsl 4 | module Ssm 5 | # This method grabs the ssm parameter store value at "compile" time vs 6 | # CloudFormation run time. In case we need it as part of the DSL compile phase. 7 | def ssm(name) 8 | resp = ssm_client.get_parameter(name: name) 9 | if resp.parameter.type == "SecureString" 10 | resp = ssm_client.get_parameter(name: name, with_decryption: true) 11 | end 12 | 13 | resp.parameter.value 14 | rescue Aws::SSM::Errors::ParameterNotFound 15 | puts "WARN: #{name} found on AWS SSM.".color(:yellow) 16 | end 17 | 18 | def ssm_client 19 | @ssm_client ||= Aws::SSM::Client.new 20 | end 21 | end 22 | end -------------------------------------------------------------------------------- /lib/pipedream/dsl/webhook.rb: -------------------------------------------------------------------------------- 1 | module Pipedream::Dsl 2 | module Webhook 3 | include Ssm 4 | 5 | # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-codepipeline-webhook.html 6 | PROPERTIES = %w[ 7 | authentication 8 | authentication_configuration 9 | filters 10 | name 11 | register_with_third_party 12 | target_action 13 | target_pipeline 14 | target_pipeline_version 15 | ] 16 | PROPERTIES.each do |prop| 17 | define_method(prop) do |v| 18 | @properties[prop.to_sym] = v 19 | end 20 | end 21 | 22 | def secret_token(v) 23 | @secret_token = v 24 | end 25 | alias_method :github_token, :secret_token 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /docs/vendor/font-awesome/scss/_rotated-flipped.scss: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); } 5 | .#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); } 6 | .#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); } 7 | 8 | .#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); } 9 | .#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); } 10 | 11 | // Hook for IE8-9 12 | // ------------------------- 13 | 14 | :root .#{$fa-css-prefix}-rotate-90, 15 | :root .#{$fa-css-prefix}-rotate-180, 16 | :root .#{$fa-css-prefix}-rotate-270, 17 | :root .#{$fa-css-prefix}-flip-horizontal, 18 | :root .#{$fa-css-prefix}-flip-vertical { 19 | filter: none; 20 | } 21 | -------------------------------------------------------------------------------- /docs/_sass/_download.scss: -------------------------------------------------------------------------------- 1 | // Styling for the download section 2 | section.download { 3 | color: white; 4 | position: relative; 5 | padding: 80px 0; 6 | h2 { 7 | font-size: 50px; 8 | margin-top: 0; 9 | } 10 | .badges { 11 | .badge-link { 12 | display: block; 13 | margin-bottom: 25px; 14 | &:last-child { 15 | margin-bottom: 0; 16 | } 17 | img { 18 | height: 60px; 19 | } 20 | @media(min-width: 768px) { 21 | display: inline-block; 22 | margin-bottom: 0; 23 | } 24 | } 25 | } 26 | @media(min-width: 768px) { 27 | h2 { 28 | font-size: 70px; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /docs/_sass/_variables.scss: -------------------------------------------------------------------------------- 1 | // Variables 2 | 3 | // Gray and Brand Colors for use across theme 4 | 5 | $theme-primary: #777; 6 | $theme-secondary: #7b4397; 7 | $theme-tertiary: #dc2430; 8 | 9 | $gray-base: #000; 10 | $gray-darker: lighten($gray-base, 13.5%); // #222 11 | $gray-dark: lighten($gray-base, 20%); // #333 12 | $gray: lighten($gray-base, 33.5%); // #555 13 | $gray-light: lighten($gray-base, 46.7%); // #777 14 | $gray-lighter: lighten($gray-base, 93.5%); // #eee 15 | 16 | $brand-twitter: #1da1f2; 17 | $brand-facebook: #3b5998; 18 | $brand-google-plus: #dd4b39; 19 | 20 | $site-color-primary: #3972c7; 21 | $site-color-primary-rgb: #18FF9C; 22 | $site-color-secondary: #2c3e50; 23 | $site-color-secondary-dark: #233140; 24 | $site-color-links: #a3c8ff; 25 | -------------------------------------------------------------------------------- /lib/pipedream/delete.rb: -------------------------------------------------------------------------------- 1 | module Pipedream 2 | class Delete 3 | include AwsServices 4 | 5 | def initialize(options) 6 | @options = options 7 | @pipeline_name = options[:pipeline_name] || inferred_pipeline_name 8 | @stack_name = options[:stack_name] || inferred_stack_name(@pipeline_name) 9 | end 10 | 11 | def run 12 | message = "Deleted #{@stack_name} stack." 13 | if @options[:noop] 14 | puts "NOOP #{message}" 15 | else 16 | are_you_sure?(@stack_name, :delete) 17 | 18 | if stack_exists?(@stack_name) 19 | cfn.delete_stack(stack_name: @stack_name) 20 | puts message 21 | else 22 | puts "#{@stack_name.inspect} stack does not exist".color(:red) 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /docs/_includes/content.html: -------------------------------------------------------------------------------- 1 | {% if page.subnav == false %} 2 | 3 | {{ content }} 4 | 5 | {% else %} 6 | 7 |
8 |
9 |
10 | {% if page.reference %} 11 | 14 | {% endif %} 15 |
16 |

{{ page.title }}

17 | {{ content }} 18 | {% include edit-on-github.html %} 19 |
20 | {% include subnav.html %} 21 |
22 |
23 |
24 | 25 | {% endif %} 26 | -------------------------------------------------------------------------------- /docs/_includes/edit-on-github.html: -------------------------------------------------------------------------------- 1 |
2 |

Edit this page

3 |

See a typo or an error? You can improve this page. This website is available on GitHub, and contributions are encouraged and welcomed. We love pull requests from you!

4 | 9 |
10 | -------------------------------------------------------------------------------- /docs/_reference/pipe-completion.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: pipe completion 3 | reference: true 4 | --- 5 | 6 | ## Usage 7 | 8 | pipe completion *PARAMS 9 | 10 | ## Description 11 | 12 | Prints words for auto-completion. 13 | 14 | Example: 15 | 16 | pipe completion 17 | 18 | Prints words for TAB auto-completion. 19 | 20 | Examples: 21 | 22 | pipe completion 23 | pipe completion hello 24 | pipe completion hello name 25 | 26 | To enable, TAB auto-completion add the following to your profile: 27 | 28 | eval $(pipe completion_script) 29 | 30 | Auto-completion example usage: 31 | 32 | pipe [TAB] 33 | pipe hello [TAB] 34 | pipe hello name [TAB] 35 | pipe hello name --[TAB] 36 | 37 | 38 | ## Options 39 | 40 | ``` 41 | [--verbose], [--no-verbose] 42 | [--noop], [--no-noop] 43 | ``` 44 | 45 | -------------------------------------------------------------------------------- /docs/_docs/next-steps.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Next Steps 3 | nav_order: 23 4 | --- 5 | 6 | Hopefully, you have a good feel for how Pipe Dream works now. From here, there are a few resources that can help you continue along: 7 | 8 | * Check out the [pipedream](https://github.com/tongueroo/pipedream) repo on GitHub 9 | * ⭐️ the pipedream project on GitHub 10 | * Write a blog post about pipedream 11 | * Post on your favorite discussion about pipedream 12 | * Contribute a pull request 13 | 14 | Everyone can contribute to making pipedream better, including the documentation. These docs are the pipedream repo located the [docs folder](https://github.com/tongueroo/pipedream/tree/master/docs). Please fork the project and open a pull request! We love your pull requests. Contributions are encouraged and welcomed! 15 | 16 | {% include prev_next.md %} 17 | -------------------------------------------------------------------------------- /docs/vendor/font-awesome/less/path.less: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('@{fa-font-path}/fontawesome-webfont.eot?v=@{fa-version}'); 7 | src: url('@{fa-font-path}/fontawesome-webfont.eot?#iefix&v=@{fa-version}') format('embedded-opentype'), 8 | url('@{fa-font-path}/fontawesome-webfont.woff2?v=@{fa-version}') format('woff2'), 9 | url('@{fa-font-path}/fontawesome-webfont.woff?v=@{fa-version}') format('woff'), 10 | url('@{fa-font-path}/fontawesome-webfont.ttf?v=@{fa-version}') format('truetype'), 11 | url('@{fa-font-path}/fontawesome-webfont.svg?v=@{fa-version}#fontawesomeregular') format('svg'); 12 | // src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | -------------------------------------------------------------------------------- /docs/_docs/install.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Installation 3 | nav_order: 3 4 | --- 5 | 6 | ## RubyGems 7 | 8 | Install pipedream via RubyGems. 9 | 10 | gem install pipedream 11 | 12 | The [Quick Start]({% link quick-start.md %}) provides a guide on how to use the codebuild tool. The [DSL Docs]({% link _docs/dsl.md %}) provide more detail on the syntax. 13 | 14 | ## AWS CLI 15 | 16 | AWS access is required. Though you can set up acess with `AWS_*` environment variables, the AWS CLI is strongly recommended. It'll allow the use of AWS Profiles. You can install the [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/installing.html) via pip. 17 | 18 | pip install awscli --upgrade --user 19 | 20 | Then [configure it](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html). 21 | 22 | aws configure 23 | 24 | {% include prev_next.md %} 25 | -------------------------------------------------------------------------------- /docs/vendor/font-awesome/scss/_path.scss: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}'); 7 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') format('embedded-opentype'), 8 | url('#{$fa-font-path}/fontawesome-webfont.woff2?v=#{$fa-version}') format('woff2'), 9 | url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') format('woff'), 10 | url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') format('truetype'), 11 | url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') format('svg'); 12 | // src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | -------------------------------------------------------------------------------- /docs/vendor/font-awesome/less/animated.less: -------------------------------------------------------------------------------- 1 | // Animated Icons 2 | // -------------------------- 3 | 4 | .@{fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | .@{fa-css-prefix}-pulse { 10 | -webkit-animation: fa-spin 1s infinite steps(8); 11 | animation: fa-spin 1s infinite steps(8); 12 | } 13 | 14 | @-webkit-keyframes fa-spin { 15 | 0% { 16 | -webkit-transform: rotate(0deg); 17 | transform: rotate(0deg); 18 | } 19 | 100% { 20 | -webkit-transform: rotate(359deg); 21 | transform: rotate(359deg); 22 | } 23 | } 24 | 25 | @keyframes fa-spin { 26 | 0% { 27 | -webkit-transform: rotate(0deg); 28 | transform: rotate(0deg); 29 | } 30 | 100% { 31 | -webkit-transform: rotate(359deg); 32 | transform: rotate(359deg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /docs/js/new-age.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Start Bootstrap - New Age v4.0.0-alpha (http://startbootstrap.com/template-overviews/new-age) 3 | * Copyright 2013-2017 Start Bootstrap 4 | * Licensed under MIT (https://github.com/BlackrockDigital/startbootstrap-new-age/blob/master/LICENSE) 5 | */ 6 | !function(a){"use strict";a('a[href*="#"]:not([href="#"])').click(function(){if(location.pathname.replace(/^\//,"")==this.pathname.replace(/^\//,"")&&location.hostname==this.hostname){var n=a(this.hash);if(n=n.length?n:a("[name="+this.hash.slice(1)+"]"),n.length)return a("html, body").animate({scrollTop:n.offset().top-48},1e3,"easeInOutExpo"),!1}}),a("body").scrollspy({target:"#mainNav",offset:54}),a(".navbar-collapse>ul>li>a").click(function(){a(".navbar-collapse").collapse("hide")}),a(window).scroll(function(){a("#mainNav").offset().top>100?a("#mainNav").addClass("navbar-shrink"):a("#mainNav").removeClass("navbar-shrink")})}(jQuery); -------------------------------------------------------------------------------- /docs/vendor/font-awesome/scss/_animated.scss: -------------------------------------------------------------------------------- 1 | // Spinning Icons 2 | // -------------------------- 3 | 4 | .#{$fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | .#{$fa-css-prefix}-pulse { 10 | -webkit-animation: fa-spin 1s infinite steps(8); 11 | animation: fa-spin 1s infinite steps(8); 12 | } 13 | 14 | @-webkit-keyframes fa-spin { 15 | 0% { 16 | -webkit-transform: rotate(0deg); 17 | transform: rotate(0deg); 18 | } 19 | 100% { 20 | -webkit-transform: rotate(359deg); 21 | transform: rotate(359deg); 22 | } 23 | } 24 | 25 | @keyframes fa-spin { 26 | 0% { 27 | -webkit-transform: rotate(0deg); 28 | transform: rotate(0deg); 29 | } 30 | 100% { 31 | -webkit-transform: rotate(359deg); 32 | transform: rotate(359deg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /docs/_docs/examples/codebuild-project.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Create CodeBuild Project 3 | nav_text: Create CodeBuild 4 | categories: examples 5 | nav_order: 18 6 | --- 7 | 8 | It is common to use CodeBuild as a Stage Action in the pipeline. Here's are instructions to quickly create a CodeBuild project. 9 | 10 | We'll use the [cody.run](https://cody.run) tool to help with this. Here are the commands. 11 | 12 | gem install cody # installs cody command 13 | cody init # generates starter .cody files including the buildspec.yml 14 | # edit and commit the .cody files 15 | git add .cody 16 | git commit -m 'add .cody files' 17 | git push 18 | cody deploy demo # creates a codebuild project 19 | 20 | There's also an example where we quickly create 4 test codebuild projects here: [Multiple CodeBuild Projects](https://pipedream.run/docs/examples/multiple-codebuild-projects/). 21 | 22 | {% include prev_next.md %} 23 | -------------------------------------------------------------------------------- /docs/_includes/prev_next.md: -------------------------------------------------------------------------------- 1 | {% assign all_pages = site.pages | concat: site.docs | where_exp: "item", "item.nav_order" %} 2 | {% assign links = all_pages | sort: "nav_order" %} 3 | {% for link in links %} 4 | {% if link.url == page.url %} 5 | {% unless forloop.first %} 6 | {% assign prev = tmpprev %} 7 | {% endunless %} 8 | {% if forloop.last %} 9 | {% assign last = "/reference" %} 10 | {% else %} 11 | {% assign next = links[forloop.index] %} 12 | {% endif %} 13 | {% endif %} 14 | {% assign tmpprev = link %} 15 | {% endfor %} 16 | 17 | {% if prev %}{% endif %}{% if last %}{% endif %} 18 | {% if next %}{% endif %} 19 |

Pro tip: Use the <- and -> arrow keys to move back and forward.

20 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Pipe Dream Documentation 2 | 3 | This project powers the Pipe Dream documementation website: [https://pipedream.run](https://pipedream.run). It is a static website generated by [Jekyll](https://jekyllrb.com/). 4 | 5 | ## Contributing 6 | 7 | For minor changes like typos, you can click **Suggest an edit to this page**, located at the bottom of each article. This will take you to the source file on GitHub, where you can submit a pull request for your change through the UI. 8 | 9 | ## Local Setup 10 | 11 | For larger fixes, you can run the site locally with the following: 12 | 13 | git clone https://github.com/tongueroo/pipedream.git 14 | cd pipedream/docs 15 | bundle 16 | bin/web 17 | 18 | You'll be able to view the site on [http://localhost:4000](http://localhost:4000). 19 | 20 | ## Rordering Site Nav 21 | 22 | To reorder the `nav_order` for links on the sidenav run: 23 | 24 | cd docs 25 | jekyll-sort reorder 26 | -------------------------------------------------------------------------------- /lib/pipedream/help/start.md: -------------------------------------------------------------------------------- 1 | You can start a pipeline with the `pipe start` command. Here's an example: 2 | 3 | $ pipe start 4 | Pipeline started: demo 5 | Please check the CodePipeline console for the status. 6 | CodePipeline Console: https://us-west-2.console.aws.amazon.com/codesuite/codepipeline/pipelines/demo/view 7 | Pipeline cli: aws codepipeline get-pipeline-execution --pipeline-execution-id 02579d64-9271-4edc-aa45-bc9629d732bb --pipeline-name demo 8 | $ 9 | 10 | ## Specifying Code Branch 11 | 12 | If you would like start a build using a specific code branch you can use the `--branch` or `-b` option. Example: 13 | 14 | pipe start -b feature-branch 15 | 16 | ## AWS CLI Equivalent 17 | 18 | The `pipe start` command is a simple wrapper to the AWS API with the ruby sdk. You can also start pipelines with the `aws codepipeline` cli. Here's the equivalent CLI command: 19 | 20 | aws codepipeline start-pipeline-execution --name demo 21 | -------------------------------------------------------------------------------- /docs/_docs/structure.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Structure 3 | nav_order: 4 4 | --- 5 | 6 | The `pipe init` command generates the initial directory structure that looks like this: 7 | 8 | .pipedream 9 | ├── pipeline.rb 10 | ├── role.rb 11 | ├── schedule.rb 12 | ├── settings.yml 13 | └── webhook.rb 14 | 15 | The table below states the purpose of each file: 16 | 17 | File / Directory | Description 18 | ------------- | ------------- 19 | pipeline.rb | The pipeline defintion. More info: [Pipeline DSL]({% link _docs/dsl/pipeline.md %}) 20 | role.rb | The IAM role defintion. More info: [Role DSL]({% link _docs/dsl/role.md %}) 21 | schedule.rb | The schedule defintion. More info: [Schedule DSL]({% link _docs/dsl/schedule.md %}) 22 | settings.yml | Settings for pipedream. More info: [Settings]({% link _docs/settings.md %}) 23 | webhook.rb | The webhook defintion. More info: [Webhook DSL]({% link _docs/dsl/webhook.md %}) 24 | 25 | {% include prev_next.md %} 26 | -------------------------------------------------------------------------------- /docs/_docs/dsl/schedule.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Schedule DSL 3 | nav_text: Schedule 4 | categories: dsl 5 | nav_order: 15 6 | --- 7 | 8 | Pipe Dream supports creating a CloudWatch scheduled event rule that will trigger the pipeline periodically. You define the schedule in `.pipedream/schedule.rb`. Here's an example of what that looks like: 9 | 10 | .pipedream/schedule.rb: 11 | 12 | ```ruby 13 | rate "1 day" 14 | # or 15 | # cron("0 10 * * ? *") # Run at 10:00 am (UTC) every day 16 | ``` 17 | 18 | ## Full DSL 19 | 20 | The convenience methods merely wrap properties of the [AWS::Events::Rule](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-events-rule.html#cfn-events-rule-description). If you wanted to set the CloudFormation properties more directly, here's an example of using the Full DSL. 21 | 22 | .pipedream/schedule.rb: 23 | 24 | ```ruby 25 | description "my description" 26 | schedule_expression "rate(1 day)" 27 | ``` 28 | 29 | {% include prev_next.md %} 30 | -------------------------------------------------------------------------------- /docs/_sass/_bootstrap-overrides.scss: -------------------------------------------------------------------------------- 1 | // Bootstrap overrides for this template 2 | .bg-primary { 3 | // background: $theme-primary; 4 | // background: -webkit-linear-gradient($theme-primary, darken($theme-primary, 5%)); 5 | // background: linear-gradient(#c70000, darken($theme-primary, 5%)); 6 | background-color: #730c0c !important; 7 | } 8 | 9 | .text-primary { 10 | color: $theme-primary; 11 | } 12 | 13 | .no-gutter > [class*='col-'] { 14 | padding-right: 0; 15 | padding-left: 0; 16 | } 17 | 18 | .btn-outline { 19 | color: white; 20 | border: 1px solid; 21 | border-color: white; 22 | &:hover, 23 | &:focus, 24 | &:active, 25 | &.active { 26 | color: white; 27 | border-color: $theme-primary; 28 | background-color: $theme-primary; 29 | } 30 | } 31 | 32 | .btn { 33 | border-radius: 300px; 34 | @include alt-font; 35 | } 36 | 37 | .btn-xl { 38 | font-size: 11px; 39 | padding: 15px 45px; 40 | } 41 | -------------------------------------------------------------------------------- /docs/_docs/dsl/webhook.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Webhook DSL 3 | nav_text: Webhook 4 | categories: dsl 5 | nav_order: 17 6 | --- 7 | 8 | The simplest way to declare a webhook is to use the `github_token` method. This single line is enough to configure and set up the webhook. 9 | 10 | .pipedream/webhook.rb: 11 | 12 | ```ruby 13 | github_token(ssm("/codepipeline/github/token")) 14 | ``` 15 | 16 | ## Full DSL 17 | 18 | The convenience methods merely wrap properties of the [AWS::CodePipeline::Webhook](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-codepipeline-webhook.html). If you wanted to set the CloudFormation properties more directly, here's an example of using the Full DSL. 19 | 20 | .pipedream/webhook.rb: 21 | 22 | ```ruby 23 | authentication "GITHUB_HMAC" 24 | authentication_configuration(secret_token: ssm("/codepipeline/github/token")) 25 | filters([{ 26 | json_path: "$.ref", 27 | match_equals: "refs/heads/{Branch}", 28 | }]) 29 | ``` 30 | 31 | {% include prev_next.md %} 32 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | ENV["TEST"] = "1" 2 | 3 | # Ensures aws api never called. Fixture home folder does not contain ~/.aws/credentails 4 | ENV['HOME'] = File.join(Dir.pwd,'spec/fixtures/home') 5 | 6 | ENV['PIPE_ROOT'] = "spec/fixtures/app" 7 | 8 | 9 | # CodeClimate test coverage: https://docs.codeclimate.com/docs/configuring-test-coverage 10 | # require 'simplecov' 11 | # SimpleCov.start 12 | 13 | require "pp" 14 | require "byebug" 15 | root = File.expand_path("../", File.dirname(__FILE__)) 16 | require "#{root}/lib/pipedream" 17 | 18 | module Helper 19 | def execute(cmd) 20 | puts "Running: #{cmd}" if show_command? 21 | out = `#{cmd}` 22 | puts out if show_command? 23 | out 24 | end 25 | 26 | # Added SHOW_COMMAND because DEBUG is also used by other libraries like 27 | # bundler and it shows its internal debugging logging also. 28 | def show_command? 29 | ENV['DEBUG'] || ENV['SHOW_COMMAND'] 30 | end 31 | end 32 | 33 | RSpec.configure do |c| 34 | c.include Helper 35 | end 36 | -------------------------------------------------------------------------------- /docs/_docs/dsl/pipeline/action.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Action General Method 3 | nav_text: Action 4 | categories: dsl-pipeline 5 | nav_order: 11 6 | --- 7 | 8 | The `action` method is a generalized way to add Actions to pipeline Stages. Generally, it is recommended to use the helper methods like [codebuild]({% link _docs/dsl/pipeline/codebuild.md %}) and [approve]({% link _docs/dsl/pipeline/approve.md %}) when possible to keep code concise. Here's an example of a pipeline using the generalized `action` method. 9 | 10 | ```ruby 11 | stage "Build" do 12 | action( 13 | name: "action1", 14 | action_type_id: { 15 | category: "Build", 16 | owner: "AWS", 17 | provider: "CodeBuild", 18 | version: "1", 19 | }, 20 | run_order: 1, 21 | configuration: { project_name: 'demo1' }, 22 | # output_artifacts: [name: "BuildArtifact#{name}"], # optional 23 | input_artifacts: [name: "SourceArtifact"], # SourceArtifact is the default primary source 24 | ) 25 | end 26 | ``` 27 | 28 | {% include prev_next.md %} 29 | -------------------------------------------------------------------------------- /docs/_sass/_cta.scss: -------------------------------------------------------------------------------- 1 | // Styling for the call to action section 2 | section.cta { 3 | position: relative; 4 | padding: 50px 0; 5 | text-align: center; 6 | margin: 0 auto; 7 | // background-image: url('../img/bg-cta.jpg'); 8 | background-position: center; 9 | @include background-cover; 10 | .cta-content { 11 | position: relative; 12 | z-index: 1; 13 | h2 { 14 | text-align: center; 15 | margin: 0 auto 25px auto; 16 | font-size: 50px; 17 | max-width: 450px; 18 | color: white; 19 | } 20 | @media (min-width: 768px) { 21 | h2 { 22 | text-align: center; 23 | margin: 0 auto; 24 | font-size: 80px; 25 | } 26 | } 27 | } 28 | .overlay { 29 | position: absolute; 30 | top: 0; 31 | left: 0; 32 | width: 100%; 33 | height: 100%; 34 | // background-color: fade-out(black, .5); 35 | background-color: #730c0c; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /spec/lib/pipeline/approve_spec.rb: -------------------------------------------------------------------------------- 1 | describe Pipedream::Pipeline do 2 | let(:stack) do 3 | stack = Pipedream::Stack.new( 4 | pipeline_path: "spec/fixtures/pipelines/#{pipeline_example}.rb", 5 | stack_name: "fake", # to avoid .pipedream check in settings 6 | ) 7 | allow(stack).to receive(:perform) 8 | allow(stack).to receive(:url_info) 9 | stack 10 | end 11 | 12 | context "pipeline with approve step without existing sns topic arn" do 13 | let(:pipeline_example) { "approve" } 14 | it "stack" do 15 | stack.run 16 | template = stack.instance_variable_get(:@template) 17 | # creates and manages the SnsTopic 18 | expect(template["Resources"]["SnsTopic"]).to be_a(Hash) 19 | end 20 | end 21 | 22 | context "pipeline with approve step with existing sns topic arn" do 23 | let(:pipeline_example) { "approve_existing_sns" } 24 | 25 | it "stack" do 26 | stack.run 27 | template = stack.instance_variable_get(:@template) 28 | # does not creates the SnsTopic 29 | expect(template["Resources"]["SnsTopic"]).to be nil 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/pipedream/dsl/pipeline.rb: -------------------------------------------------------------------------------- 1 | module Pipedream::Dsl 2 | module Pipeline 3 | include Approve 4 | include Codebuild 5 | include Github 6 | include Ssm 7 | 8 | PROPERTIES = %w[ 9 | artifact_store 10 | artifact_stores 11 | disable_inboundstage_transitions 12 | input_artifacts 13 | name 14 | restart_execution_on_update 15 | role_arn 16 | stages 17 | ] 18 | PROPERTIES.each do |prop| 19 | define_method(prop) do |v| 20 | @properties[prop.to_sym] = v 21 | end 22 | end 23 | 24 | def pipeline_name 25 | @options[:pipeline_name] 26 | end 27 | 28 | def stage(name, &block) 29 | # Reset values for each stage declaraion 30 | @run_order = 1 31 | 32 | @current_stage = {name: name, actions: []} 33 | @stages << @current_stage 34 | block.call 35 | end 36 | 37 | def in_parallel 38 | @in_parallel = true 39 | yield 40 | @in_parallel = false 41 | end 42 | 43 | def action(*props) 44 | @current_stage[:actions] += props 45 | @run_order += 1 unless @in_parallel 46 | end 47 | end 48 | end -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Tung Nguyen 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /docs/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Tung Nguyen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/_docs/settings.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Settings 3 | nav_order: 7 4 | --- 5 | 6 | The `.pipedream/settings.yml` file can be used to adjust some of the behavior of pipedream. Here's an example of a settings.yml file: 7 | 8 | ```yaml 9 | base: 10 | # stack_naming: 11 | # append_env: true # default false 12 | 13 | development: 14 | # aws_profile: dev_profile 15 | 16 | production: 17 | # aws_profile: prod_profile 18 | ``` 19 | 20 | The base settings are common and used for all the environments. The other environments are used according to the value of `PIPE_ENV`. 21 | 22 | ## Example 23 | 24 | pipe deploy # will use the development settings since development is the default 25 | PIPE_ENV=production pipe deploy # will use the production settings 26 | 27 | ## Options 28 | 29 | Name | Description 30 | --- | --- 31 | stack_naming.append_env | Determines if `PIPE_ENV` value is append to the pipeline name. 32 | aws_profile | This provides a way to bind `PIPE_ENV` to `AWS_PROFILE` tightly. This prevents you from forgetting to switch your `PIPE_ENV` when switching your `AWS_PROFILE`, thereby accidentally launching a stack in the wrong environment. 33 | 34 | {% include prev_next.md %} 35 | -------------------------------------------------------------------------------- /lib/pipedream/dsl/pipeline/approve.rb: -------------------------------------------------------------------------------- 1 | module Pipedream::Dsl::Pipeline 2 | module Approve 3 | def approve(props) 4 | default = { 5 | name: "approve", 6 | action_type_id: { 7 | category: "Approval", 8 | owner: "AWS", 9 | provider: "Manual", 10 | version: "1", 11 | }, 12 | run_order: @run_order, 13 | configuration: { # required: will be set 14 | notification_arn: {ref: "SnsTopic"}, # defaults to generated SNS topic 15 | }, 16 | } 17 | 18 | # Normalize special options. Simple approach of setting the default 19 | case props 20 | when String, Symbol 21 | default[:configuration][:custom_data] = props 22 | props = {} 23 | when Hash 24 | default[:configuration][:notification_arn] = props.delete(:notification_arn) if props.key?(:notification_arn) 25 | default[:configuration][:custom_data] = props.delete(:custom_data) if props.key?(:custom_data) 26 | else 27 | raise "Invalid props type: #{props.class}" 28 | end 29 | 30 | options = default.merge(props) 31 | action(options) 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /docs/docs.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Overview 3 | nav_order: 2 4 | --- 5 | 6 | ## What is Pipe Dream? 7 | 8 | Pipe Dream is a tool that provides a powerful DSL to simplify creating and managing [AWS CodePipeline](https://aws.amazon.com/codepipeline/) resources. You can create a Pipeline, Scheduled Event, IAM Role, and Webhook. 9 | 10 | The [pipeline DSL]({% link _docs/dsl.md %}) is essentially a wrapper to CloudFormation for resources like the [CodePipeline Project resource](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-codepipeline-pipeline.html). This means you can **fully control** and customize of the CodePipeline resources. 11 | 12 | ## Usage Scenarios 13 | 14 | Here are some ways to use Pipe Dream: 15 | 16 | * continuous integration and delivery 17 | * visualizing the deploy flow 18 | * building artifacts: Docker images, AMIs, jars, s3 objects, etc 19 | 20 | ## AWS CodePipeline vs CodeBuild 21 | 22 | AWS CodePipeline is higher-level software than CodeBuild. CodeBuild is a managed build service, and you can use it to automate tasks. CodePipeline helps you sequence the steps and puts it all together; providing you with a high-level visualization. 23 | 24 | {% include prev_next.md %} 25 | -------------------------------------------------------------------------------- /docs/_includes/tutorials.md: -------------------------------------------------------------------------------- 1 | ## Introducing Jets 2 | 3 |
4 | 5 | ## Jets Introduction Series 6 | 7 | Introductions are focused on AWS fundamental essentials. 8 | 9 | {% assign posts = site.data.intro_series %} 10 | {% for post in posts %} 11 | * [{{ post.title }}]({{ post.url }}){% endfor %} 12 | 13 | ## Jets Tutorial Series 14 | 15 | Tutorials are focused on Jets fundamentals. 16 | 17 | {% assign posts = site.data.tutorial_series %} 18 | {% for post in posts %} 19 | * [{{ post.title }}]({{ post.url }}){% endfor %} 20 | 21 | ## Jets Articles 22 | 23 | Articles, tutorials, and demos on Jets. 24 | 25 | {% assign posts = site.data.articles %} 26 | {% for post in posts %} 27 | * [{{ post.title }}]({{ post.url }}){% endfor %} 28 | 29 | ## Tutorials 30 | 31 | * [HTML ActiveRecord Tutorial]({% link _docs/crud-html-activerecord.md %}) 32 | * [JSON ActiveRecord Tutorial]({% link _docs/crud-json-activerecord.md %}) 33 | 34 | ## Videos 35 | 36 | {% assign posts = site.data.video_playlists %} 37 | {% for post in posts %} 38 | * [{{ post.title }}]({{ post.url }}){% endfor %} 39 | -------------------------------------------------------------------------------- /lib/template/.pipedream/pipeline.rb.tt: -------------------------------------------------------------------------------- 1 | stage "Source" do 2 | github( 3 | source: "<%= project_github_repo %>", 4 | # branch: "master", # branch defaults to "master" or the `pipe deploy --branch` option 5 | auth_token: ssm("/codepipeline/github/token") # example ssm name 6 | ) 7 | end 8 | 9 | # IMPORANT: A valid pipeline requires at least 2 stages before you are able to run 10 | # 11 | # pipe deploy 12 | # 13 | # Here are some possible examples below. If you need help creating a CodeBuild project one, check out 14 | # https://pipedream.run/docs/examples/codebuild-project/ 15 | 16 | # stage "Build" do 17 | # codebuild "demo" 18 | # end 19 | 20 | # stage "MoreBuilds" do 21 | # codebuild "project1" 22 | # codebuild "project2", "project3" # runs in parallel 23 | # codebuild "project4" 24 | # end 25 | 26 | # stage "Approve" do 27 | # # Existing SNS Topic 28 | # # approve( 29 | # # notification_arn: "arn:aws:sns:us-west-2:536766270177:hello-topic", 30 | # # custom_data: "Approve deployment", 31 | # # ) 32 | # 33 | # # OR 34 | # # CodePipeline will create and managed the SNS Topoic 35 | # approve("Approve deployment") 36 | # end 37 | 38 | # stage "Deploy" do 39 | # codebuild "project5" 40 | # end 41 | -------------------------------------------------------------------------------- /docs/_includes/nav.html: -------------------------------------------------------------------------------- 1 | 2 | 18 | -------------------------------------------------------------------------------- /docs/_sass/_footer.scss: -------------------------------------------------------------------------------- 1 | // Styling for the footer 2 | footer { 3 | padding: 25px 0; 4 | text-align: center; 5 | background-color: $gray-darker; 6 | color: white; 7 | p { 8 | margin: 0; 9 | } 10 | ul { 11 | margin-bottom: 0; 12 | li { 13 | a { 14 | font-size: 1.2em; 15 | color: white; 16 | &:hover, 17 | &:focus, 18 | &:active, 19 | &.active { 20 | text-decoration: none; 21 | } 22 | } 23 | } 24 | } 25 | 26 | ul.list-inline { 27 | li { 28 | display: inline; 29 | margin: 0 5px; 30 | a.btn-social { 31 | display: inline-block; 32 | width: 50px; 33 | height: 50px; 34 | border: 2px solid #fff; 35 | border-radius: 100%; 36 | text-align: center; 37 | font-size: 20px; 38 | line-height: 45px; 39 | } 40 | } 41 | } 42 | } 43 | 44 | .footer-col { 45 | padding-top: 20px; 46 | @media(min-width: 992px) { 47 | padding-top: 10px; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/pipedream/deploy.rb: -------------------------------------------------------------------------------- 1 | module Pipedream 2 | class Deploy < Stack 3 | def run 4 | handle_rollback_completed! 5 | if stack_exists?(@stack_name) 6 | Update.new(@options).run 7 | else 8 | Create.new(@options).run 9 | end 10 | end 11 | 12 | def handle_rollback_completed! 13 | @stack = find_stack(@stack_name) 14 | if @stack && rollback_complete?(@stack) 15 | puts "Existing stack in ROLLBACK_COMPLETE state. Deleting stack before continuing." 16 | cfn.delete_stack(stack_name: @stack_name) 17 | status.wait 18 | status.reset 19 | @stack = nil # at this point stack has been deleted 20 | end 21 | end 22 | 23 | def rollback_complete?(stack) 24 | stack.stack_status == 'ROLLBACK_COMPLETE' 25 | end 26 | 27 | def find_stack(stack_name) 28 | return if ENV['TEST'] 29 | resp = cfn.describe_stacks(stack_name: stack_name) 30 | resp.stacks.first 31 | rescue Aws::CloudFormation::Errors::ValidationError => e 32 | # example: Stack with id demo-web does not exist 33 | if e.message =~ /Stack with/ && e.message =~ /does not exist/ 34 | nil 35 | else 36 | raise 37 | end 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /docs/js/nav.js: -------------------------------------------------------------------------------- 1 | function onlyShow(className) { 2 | $("ul.build-tutorial").hide(); 3 | $("ul.build-new").hide(); 4 | $("ul.build-existing").hide(); 5 | if (className) { 6 | $("ul." + className).show(); 7 | } 8 | } 9 | 10 | $( document ).ready(function() { 11 | var currentPath = $(location).attr('pathname'); 12 | 13 | // add active class to the subnav link based on the current page 14 | var activeLink = $(".content-nav a").filter(function(i, link) { 15 | return(link.pathname == currentPath); 16 | }); 17 | activeLink.addClass("active"); 18 | 19 | // allows use to use arrow keys to move back and forward through the docs 20 | var keymap = {}; 21 | 22 | // LEFT 23 | keymap[ 37 ] = "#prev"; 24 | // RIGHT 25 | keymap[ 39 ] = "#next"; 26 | 27 | $( document ).on( "keyup", function(event) { 28 | var href, 29 | selector = keymap[ event.which ]; 30 | // if the key pressed was in our map, check for the href 31 | if ( selector ) { 32 | href = $( selector ).attr( "href" ); 33 | if ( href ) { 34 | // navigate where the link points 35 | window.location = href; 36 | } 37 | } 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /lib/pipedream/sns.rb: -------------------------------------------------------------------------------- 1 | module Pipedream 2 | class Sns 3 | include Pipedream::Dsl::Sns 4 | include Evaluate 5 | 6 | def initialize(options={}) 7 | @options = options 8 | @sns_path = options[:sns_path] || get_sns_path 9 | @properties = default_properties 10 | end 11 | 12 | def run 13 | evaluate(@sns_path) if File.exist?(@sns_path) 14 | 15 | resource = { 16 | sns_topic: { 17 | type: "AWS::SNS::Topic", 18 | properties: @properties 19 | } 20 | } 21 | CfnCamelizer.transform(resource) 22 | end 23 | 24 | def default_properties 25 | display_name = "#{@options[:full_pipeline_name]} pipeline" 26 | { 27 | display_name: display_name, 28 | # kms_master_key_id: "string", 29 | # subscription: [{ 30 | # endpoint: '', 31 | # protocol: ',' 32 | # }], 33 | # topic_name: "string", # Not setting because update requires: Replacement. Dont want 2 pipelines to collide 34 | } 35 | # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sns-subscription.html 36 | end 37 | 38 | private 39 | def get_sns_path 40 | lookup_pipedream_file("sns.rb") 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /docs/_docs/dsl.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Pipe Dream DSL 3 | nav_order: 9 4 | --- 5 | 6 | Pipe Dream provides a simple yet powerful DSL to create CodePipeline related resources. Here's an example: 7 | 8 | ```ruby 9 | stage "Source" do 10 | github( 11 | source: "tongueroo/demo-test", 12 | auth_token: ssm("/github/user/token") 13 | ) 14 | end 15 | 16 | stage "Build" do 17 | codebuild "demo1", "demo2" 18 | codebuild "demo3" 19 | end 20 | 21 | stage "Approve" do 22 | approve("Approve this deploy") 23 | end 24 | 25 | stage "Deploy" do 26 | codebuild "deploy" 27 | end 28 | ``` 29 | 30 | Here are some examples of resources it can create: 31 | 32 | * [pipeline]({% link _docs/dsl/pipeline.md %}): The CodePipeline pipeline. This is required. 33 | * [iam role]({% link _docs/dsl/role.md %}): The IAM role associated with the CodePipeline pipeline. 34 | * [webhook]({% link _docs/dsl/webhook.md %}): The webhook associated with the CodePipeline pipeline. 35 | * [schedule]({% link _docs/dsl/schedule.md %}): An CloudWatch Event rule: triggers the pipeline to start on a scheduled basis. 36 | * [sns topic]({% link _docs/dsl/sns.md %}): The SNS Topic associated with the approval step. This is optional and provides a way to customize the SNS topic if needed. 37 | 38 | {% include prev_next.md %} 39 | -------------------------------------------------------------------------------- /docs/_sass/_features.scss: -------------------------------------------------------------------------------- 1 | // Styling for the features section 2 | section.features { 3 | h3 { 4 | // background-color: green; 5 | // font-size: 1.6em; 6 | } 7 | .section-heading { 8 | margin-bottom: 20px; 9 | h2 { 10 | margin-top: 0; 11 | } 12 | p { 13 | margin-bottom: 0; 14 | } 15 | } 16 | .device-container, 17 | .feature-item { 18 | max-width: 300px; 19 | margin: 0 auto; 20 | } 21 | .device-container { 22 | margin-bottom: 100px; 23 | @media(min-width: 992px) { 24 | margin-bottom: 0; 25 | } 26 | } 27 | .feature-item { 28 | margin-bottom: 100px; 29 | text-align: center; 30 | h3 { 31 | font-size: 30px; 32 | } 33 | i { 34 | font-size: 80px; 35 | background: -webkit-linear-gradient(to left, $theme-secondary, $theme-tertiary); 36 | background: linear-gradient(to left, $theme-secondary, $theme-tertiary); 37 | -webkit-background-clip: text; 38 | -webkit-text-fill-color: transparent; 39 | } 40 | } 41 | @media(min-width: 992px) { 42 | .device-container, 43 | .feature-item { 44 | max-width: none; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | subnav: false 4 | --- 5 | 6 |
7 |
8 |
9 |
10 |
11 |
12 |

Pipe Dream

13 |

A powerful CodePipeline DSL. Pipe Dream simplifies creating and managing CodePipeline pipelines.

14 | Learn More! 15 |
16 |
17 |
18 |
19 | 20 |
21 |
22 |
23 |
24 | 25 | {% include example.html %} 26 | 27 | {% include commands.html %} 28 | 29 |
30 |
31 |
32 |

Learn More

33 | Quick Start 34 |
35 |
36 |
37 |
38 | -------------------------------------------------------------------------------- /docs/js/new-age.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | "use strict"; // Start of use strict 3 | 4 | // Smooth scrolling using jQuery easing 5 | $('a[href*="#"]:not([href="#"])').click(function() { 6 | if (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') && location.hostname == this.hostname) { 7 | var target = $(this.hash); 8 | target = target.length ? target : $('[name=' + this.hash.slice(1) + ']'); 9 | if (target.length) { 10 | $('html, body').animate({ 11 | scrollTop: (target.offset().top - 48) 12 | }, 1000, "easeInOutExpo"); 13 | return false; 14 | } 15 | } 16 | }); 17 | 18 | // Activate scrollspy to add active class to navbar items on scroll 19 | $('body').scrollspy({ 20 | target: '#mainNav', 21 | offset: 54 22 | }); 23 | 24 | // Closes responsive menu when a link is clicked 25 | $('.navbar-collapse>ul>li>a').click(function() { 26 | $('.navbar-collapse').collapse('hide'); 27 | }); 28 | 29 | // Collapse the navbar when page is scrolled 30 | $(window).scroll(function() { 31 | if ($("#mainNav").offset().top > 10) { 32 | $("#mainNav").addClass("navbar-shrink"); 33 | } else { 34 | $("#mainNav").removeClass("navbar-shrink"); 35 | } 36 | }); 37 | 38 | })(jQuery); // End of use strict 39 | -------------------------------------------------------------------------------- /lib/pipedream/dsl/pipeline/codebuild.rb: -------------------------------------------------------------------------------- 1 | module Pipedream::Dsl::Pipeline 2 | module Codebuild 3 | def codebuild(*projects) 4 | default = { 5 | # name: '', # will be set 6 | action_type_id: { 7 | category: "Build", 8 | owner: "AWS", 9 | provider: "CodeBuild", 10 | version: "1", 11 | }, 12 | run_order: @run_order, 13 | # configuration: { project_name: '' }, # will be set 14 | # output_artifacts: [name: "BuildArtifact#{name}"], # TODO: maybe make this configurable with a setting 15 | input_artifacts: [name: "MainArtifact"], 16 | } 17 | 18 | actions = projects.map do |item| 19 | if item.is_a?(String) 20 | name = item 21 | default.deep_merge( 22 | name: name, 23 | configuration: { project_name: item }, 24 | ) 25 | else # Hash 26 | # With the hash notation, user needs to set: name and project_name 27 | # 28 | # codebuild(name: "action-name", project_name: "codebuild-project-names") 29 | # 30 | project_name = item.delete(:project_name) 31 | if project_name 32 | item[:configuration] = { project_name: project_name } 33 | end 34 | 35 | item[:name] ||= project_name 36 | item.reverse_merge(default) 37 | end 38 | end 39 | 40 | action(*actions) 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /docs/_docs/dsl/sns.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: SNS Topic DSL 3 | nav_text: Sns Topic 4 | categories: dsl 5 | nav_order: 16 6 | --- 7 | 8 | Pipe Dream will create an SNS Topic and associate it with your [Approval Action]({% link _docs/dsl/pipeline/approve.md %}) automatically. 9 | For example, if you have created an Approval Action in the pipeline with the `approve` method, then pipedream will create and manage the SNS topic for you. 10 | 11 | For most cases, the default SNS topic should suffice. However, if you wish to control the SNS topic properties you can do so with a `.pipedream/sns.rb` file. By declaring your own SNS topic, pipedream will use yours instead of the default one it usually creates. Here's an example: 12 | 13 | ```ruby 14 | display_name "my display_name" 15 | kms_master_key_id "String" 16 | subscription([{ 17 | endpoint: 'my@email.com', 18 | protocol: 'email', # protocol values: https://docs.aws.amazon.com/sns/latest/api/API_Subscribe.html 19 | }]) 20 | # topic_name "my-topic", # Recommend not setting because update requires: Replacement. Allow CloudFormation to set it so 2 pipelines dont have same SNS Topic name that collides 21 | ``` 22 | 23 | The methods in the `sns.rb` are simply properties of the [SNS::Topic](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sns-topic.html) CloudFormation Resource 24 | 25 | Note: Pipe Dream will only create the SNS Topic if you have declared an Approval Action in the `pipeline.rb` without specifying an existing SNS Topic ARN. 26 | 27 | {% include prev_next.md %} 28 | -------------------------------------------------------------------------------- /lib/pipedream/dsl/pipeline/github.rb: -------------------------------------------------------------------------------- 1 | module Pipedream::Dsl::Pipeline 2 | module Github 3 | def github(props) 4 | # nice shorthands 5 | source = props.delete(:source) 6 | source = extract_repo_source(source) 7 | owner,repo = source.split("/") 8 | 9 | # cli option can override this in codepipe/pipeline.rb set_source! 10 | # so cli option always gets the highest precendence 11 | branch = props.delete(:branch) || "master" # always delete branch prop 12 | 13 | o_auth_token = props.delete(:auth_token) 14 | poll_for_source_changes = props.delete(:poll_for_source_changes) || "false" 15 | 16 | source_name = props.delete(:source_name) || "Main" 17 | 18 | default = { 19 | name: source_name, 20 | action_type_id: { 21 | category: "Source", 22 | owner: "ThirdParty", 23 | provider: "GitHub", 24 | version: "1", 25 | }, 26 | run_order: @run_order, 27 | configuration: { 28 | branch: branch, 29 | o_auth_token: o_auth_token, 30 | owner: owner, 31 | poll_for_source_changes: poll_for_source_changes, 32 | repo: repo, 33 | }, 34 | output_artifacts: [name: "#{source_name}Artifact"] 35 | } 36 | action(props.reverse_merge(default)) 37 | end 38 | 39 | def extract_repo_source(url) 40 | url.sub('git@github.com:','').sub('https://github.com/','').sub(/\.git$/,'') 41 | end 42 | extend self # mainly for extract_repo_source 43 | end 44 | end -------------------------------------------------------------------------------- /docs/_sass/_contact.scss: -------------------------------------------------------------------------------- 1 | // Styling for the download section 2 | section.contact { 3 | text-align: center; 4 | h2 { 5 | margin-top: 0; 6 | margin-bottom: 25px; 7 | i { 8 | color: $brand-google-plus; 9 | } 10 | } 11 | ul.list-social { 12 | margin-bottom: 0; 13 | li { 14 | a { 15 | font-size: 40px; 16 | line-height: 80px; 17 | display: block; 18 | width: 80px; 19 | height: 80px; 20 | color: white; 21 | border-radius: 100%; 22 | } 23 | &.social-twitter { 24 | a { 25 | background-color: $brand-twitter; 26 | &:hover { 27 | background-color: darken($brand-twitter, 5%); 28 | } 29 | } 30 | } 31 | &.social-facebook { 32 | a { 33 | background-color: $brand-facebook; 34 | &:hover { 35 | background-color: darken($brand-facebook, 5%); 36 | } 37 | } 38 | } 39 | &.social-google-plus { 40 | a { 41 | background-color: $brand-google-plus; 42 | &:hover { 43 | background-color: darken($brand-google-plus, 5%); 44 | } 45 | } 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/pipedream/dsl/role.rb: -------------------------------------------------------------------------------- 1 | module Pipedream::Dsl 2 | module Role 3 | PROPERTIES = %w[ 4 | assume_role_policy_document 5 | managed_policy_arns 6 | max_session_duration 7 | path 8 | permissions_boundary 9 | policies 10 | role_name 11 | ] 12 | PROPERTIES.each do |prop| 13 | define_method(prop) do |v| 14 | @properties[prop.to_sym] = v 15 | end 16 | end 17 | 18 | # convenience wrapper methods 19 | def iam_policy(*definitions) 20 | @iam_statements = definitions.map { |definition| standardize_iam_policy(definition) } 21 | end 22 | 23 | # Returns standarized IAM statement 24 | def standardize_iam_policy(definition) 25 | case definition 26 | when String 27 | # Expands simple string from: logs => logs:* 28 | definition = "#{definition}:*" unless definition.include?(':') 29 | { 30 | action: [definition], 31 | effect: "Allow", 32 | resource: "*", 33 | } 34 | when Hash 35 | definition 36 | end 37 | end 38 | 39 | def managed_iam_policy(*definitions) 40 | @managed_policy_arns = definitions.map { |definition| standardize_managed_iam_policy(definition) } 41 | end 42 | 43 | # AmazonEC2ReadOnlyAccess => arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess 44 | def standardize_managed_iam_policy(definition) 45 | return definition if definition.include?('iam::aws:policy') 46 | 47 | "arn:aws:iam::aws:policy/#{definition}" 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /docs/_docs/start.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Start 3 | nav_order: 6 4 | --- 5 | 6 | You can start a pipeline with the `pipe start` command. Here's an example: 7 | 8 | $ pipe start 9 | Pipeline started: demo 10 | Please check the CodePipeline console for the status. 11 | CodePipeline Console: https://us-west-2.console.aws.amazon.com/codesuite/codepipeline/pipelines/demo/view 12 | Pipeline cli: aws codepipeline get-pipeline-execution --pipeline-execution-id 02579d64-9271-4edc-aa45-bc9629d732bb --pipeline-name demo 13 | $ 14 | 15 | ## Specifying Code Branch 16 | 17 | If you would like start a build using a specific code branch you can use the `--branch` or `-b` option. Example: 18 | 19 | pipe start -b feature-branch 20 | 21 | Note: When you specify a branch pipedream actually first updates the pipeline before starting the pipeline execution. This is done because CodePipeline does not natively support specifying the branch. It is discussed more here: [Using Different Branches]({% link _docs/examples/different-branches.md %}). 22 | 23 | ## AWS CLI Equivalent 24 | 25 | The `pipe start` command is a simple wrapper to the AWS API with the ruby sdk. You can also start pipelines with the `aws codepipeline` cli. Here's the equivalent CLI command: 26 | 27 | aws codepipeline start-pipeline-execution --name demo 28 | 29 | Note: There is no native branch option with the `aws codepipeline` cli. 30 | 31 | ## CLI Reference 32 | 33 | Also, for help info you can check the [pipe start]({% link _reference/pipe-start.md %}) CLI reference. 34 | 35 | {% include prev_next.md %} 36 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | This project *loosely* adheres to [Semantic Versioning](http://semver.org/), even before v1.0. 5 | 6 | ## [0.4.8] - 2022-01-07 7 | - [#5](https://github.com/boltops-tools/pipedream/pull/5) improvements for multiple sources and in_parallel method 8 | 9 | ## [0.4.7] - 2021-12-29 10 | - add rexml dependency 11 | 12 | ## [0.4.6] - 2021-12-29 13 | - [#4](https://github.com/boltops-tools/pipedream/pull/4) fix activesupport require 14 | - fix settings merge 15 | 16 | ## [0.4.5] 17 | - add aws codepipeline get-pipeline-state command hint in output also 18 | - dont autocamelize code build project name 19 | - #3 fix typo 20 | 21 | ## [0.4.4] 22 | - add mfa support for normal IAM user 23 | 24 | ## [0.4.3] 25 | - fix pipedream renaming 26 | 27 | ## [0.4.2] 28 | - fix project_name 29 | 30 | ## [0.4.1] 31 | - remove codebuild_prefix and codebuild_suffix helpers 32 | 33 | ## [0.4.0] 34 | - rename to pipedream 35 | 36 | ## [0.3.3] 37 | - allow no settings.yml file 38 | 39 | ## [0.3.2] 40 | - update vendor/aws_data 41 | 42 | ## [0.3.1] 43 | - fix gem dependencies in vendor 44 | 45 | ## [0.3.0] 46 | - update docs and cli help 47 | 48 | ## [0.2.1] 49 | - fix different branch check 50 | 51 | ## [0.2.0] 52 | - DSL: pipeline, role, schedule, webhook, sns 53 | - pipe deploy -b branch 54 | - pipe start -b branch 55 | - pipe cli commands: init deploy, start, delete 56 | - ssm support 57 | - codebuild\_prefix and codebuild\_suffix support 58 | - auto-create s3 bucket for artifacts 59 | 60 | ## [0.1.0] 61 | - Initial release. 62 | -------------------------------------------------------------------------------- /lib/pipedream/evaluate.rb: -------------------------------------------------------------------------------- 1 | module Pipedream 2 | module Evaluate 3 | def evaluate(path) 4 | source_code = IO.read(path) 5 | begin 6 | instance_eval(source_code, path) 7 | rescue Exception => e 8 | if e.class == SystemExit # allow exit to happen normally 9 | raise 10 | else 11 | task_definition_error(e) 12 | puts "\nFull error:" 13 | raise 14 | end 15 | end 16 | end 17 | 18 | private 19 | # Prints out a user friendly task_definition error message 20 | def task_definition_error(e) 21 | error_info = e.backtrace.first 22 | path, line_no, _ = error_info.split(':') 23 | line_no = line_no.to_i 24 | puts "Error evaluating #{path}:".color(:red) 25 | puts e.message 26 | puts "Here's the line in #{path} with the error:\n\n" 27 | 28 | contents = IO.read(path) 29 | content_lines = contents.split("\n") 30 | context = 5 # lines of context 31 | top, bottom = [line_no-context-1, 0].max, line_no+context-1 32 | spacing = content_lines.size.to_s.size 33 | content_lines[top..bottom].each_with_index do |line_content, index| 34 | line_number = top+index+1 35 | if line_number == line_no 36 | printf("%#{spacing}d %s\n".color(:red), line_number, line_content) 37 | else 38 | printf("%#{spacing}d %s\n", line_number, line_content) 39 | end 40 | end 41 | end 42 | 43 | def lookup_pipedream_file(name) 44 | [".pipedream", @options[:type], name].compact.join("/") 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /docs/_reference/pipe-start.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: pipe start 3 | reference: true 4 | --- 5 | 6 | ## Usage 7 | 8 | pipe start 9 | 10 | ## Description 11 | 12 | Start codebuild project. 13 | 14 | You can start a pipeline with the `pipe start` command. Here's an example: 15 | 16 | $ pipe start 17 | Pipeline started: demo 18 | Please check the CodePipeline console for the status. 19 | CodePipeline Console: https://us-west-2.console.aws.amazon.com/codesuite/codepipeline/pipelines/demo/view 20 | Pipeline cli: aws codepipeline get-pipeline-execution --pipeline-execution-id 02579d64-9271-4edc-aa45-bc9629d732bb --pipeline-name demo 21 | $ 22 | 23 | ## Specifying Code Branch 24 | 25 | If you would like start a build using a specific code branch you can use the `--branch` or `-b` option. Example: 26 | 27 | pipe start -b feature-branch 28 | 29 | ## AWS CLI Equivalent 30 | 31 | The `pipe start` command is a simple wrapper to the AWS API with the ruby sdk. You can also start pipelines with the `aws codepipeline` cli. Here's the equivalent CLI command: 32 | 33 | aws codepipeline start-pipeline-execution --name demo 34 | 35 | 36 | ## Options 37 | 38 | ``` 39 | [--sure=SURE] # Bypass are you sure prompt 40 | b, [--branch=BRANCH] # git branch 41 | [--stack-name=STACK_NAME] # Override the generated stack name. If you use this you must always specify it 42 | [--wait], [--no-wait] # Wait for operation to complete 43 | # Default: true 44 | [--verbose], [--no-verbose] 45 | [--noop], [--no-noop] 46 | ``` 47 | 48 | -------------------------------------------------------------------------------- /lib/pipedream/pipeline.rb: -------------------------------------------------------------------------------- 1 | module Pipedream 2 | class Pipeline 3 | extend Memoist 4 | include Dsl::Pipeline 5 | include Evaluate 6 | 7 | def initialize(options={}) 8 | @options = options 9 | @pipeline_path = options[:pipeline_path] || get_pipeline_path 10 | @properties = default_properties # defaults make pipeline.rb simpler 11 | @stages = [] 12 | end 13 | 14 | def run 15 | evaluate(@pipeline_path) 16 | @properties[:stages] ||= @stages 17 | set_source_branch! 18 | 19 | resource = { 20 | pipeline: { 21 | type: "AWS::CodePipeline::Pipeline", 22 | properties: @properties 23 | } 24 | } 25 | CfnCamelizer.transform(resource) 26 | end 27 | 28 | def default_properties 29 | { 30 | name: @options[:full_pipeline_name], 31 | role_arn: { "Fn::GetAtt": "IamRole.Arn" }, 32 | artifact_store: { 33 | type: "S3", 34 | location: s3_bucket, # auto creates s3 bucket 35 | } 36 | } 37 | end 38 | 39 | # cli branch option always takes highest precedence 40 | def set_source_branch! 41 | return unless @options[:branch] 42 | 43 | source_stage = @properties[:stages].first 44 | action = source_stage[:actions].first 45 | action[:configuration][:branch] = @options[:branch] 46 | end 47 | 48 | def exist? 49 | File.exist?(@pipeline_path) 50 | end 51 | 52 | def s3_bucket 53 | S3Bucket.name 54 | end 55 | 56 | private 57 | def get_pipeline_path 58 | lookup_pipedream_file "pipeline.rb" 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /docs/_docs/examples/different-branches.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Using Different Branches 3 | nav_text: Different Branches 4 | categories: examples 5 | nav_order: 19 6 | --- 7 | 8 | CodePipeline currently does not supports starting the pipeline execution with different branches natively. To get around this, we can: 9 | 10 | 1. update the pipeline before starting it. 11 | 2. create additional pipelines. 12 | 13 | Pipe Dream supports both methods. 14 | 15 | ## Update Pipeline Approach 16 | 17 | With the update pipeline approach, the pipeline gets updated if the current pipeline branch is different from the requested branch before a pipeline execution starts. Examples: 18 | 19 | pipe start -b master 20 | pipe start -b qa 21 | 22 | Note: With this approach, since the pipeline gets updated, the pipeline will maintain the last branch it was started with. 23 | 24 | ## Multiple Pipelines Approach 25 | 26 | You might normally set the branch option in your pipeline.rb. Example: 27 | 28 | ```ruby 29 | stage "Source" do 30 | github( 31 | source: "user/repo", 32 | branch: "master", 33 | auth_token: ssm("/github/user/token") 34 | ) 35 | end 36 | ``` 37 | 38 | When we deploy to create the pipelines, we can explicitly specify the branch to use. The cli option overrides the branch specified in `pipeline.rb`. Examples: 39 | 40 | pipe deploy demo-master -b master 41 | pipe deploy demo-qa -b qa 42 | 43 | This creates 2 pipelines. The demo-master pipeline will use the master branch, and the demo-qa pipeline will use the qa branch. 44 | 45 | To start the pipelines: 46 | 47 | pipe start demo-master 48 | pipe start demo-qa 49 | 50 | {% include prev_next.md %} 51 | -------------------------------------------------------------------------------- /lib/pipedream/webhook.rb: -------------------------------------------------------------------------------- 1 | module Pipedream 2 | class Webhook 3 | include Pipedream::Dsl::Webhook 4 | include Evaluate 5 | 6 | def initialize(options={}) 7 | @options = options 8 | @webhook_path = options[:webhook_path] || get_webhook_path 9 | @properties = default_properties 10 | end 11 | 12 | def run 13 | return unless File.exist?(@webhook_path) 14 | 15 | old_properties = @properties.clone 16 | evaluate(@webhook_path) 17 | set_secret_token! 18 | return if old_properties == @properties # empty webhook.rb file 19 | 20 | resource = { 21 | webhook: { 22 | type: "AWS::CodePipeline::Webhook", 23 | properties: @properties 24 | } 25 | } 26 | CfnCamelizer.transform(resource) 27 | end 28 | 29 | def default_properties 30 | { 31 | authentication: 'GITHUB_HMAC', # GITHUB_HMAC, IP and UNAUTHENTICATED 32 | authentication_configuration: { 33 | secret_token: @secret_token, 34 | }, 35 | filters: [{ 36 | json_path: "$.ref", 37 | match_equals: "refs/heads/{Branch}", 38 | }], 39 | # name: '', # optional 40 | register_with_third_party: 'true', # optional 41 | target_action: 'Source', 42 | target_pipeline: {ref: "Pipeline"}, 43 | target_pipeline_version: {"Fn::GetAtt": "Pipeline.Version"}, 44 | } 45 | end 46 | 47 | def set_secret_token! 48 | @properties.merge!( 49 | authentication_configuration: { 50 | secret_token: @secret_token 51 | } 52 | ) 53 | end 54 | private 55 | 56 | def get_webhook_path 57 | lookup_pipedream_file("webhook.rb") 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /docs/_includes/head.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {% if page.title %} 9 | {{ page.title }} - {{ site.title }} 10 | {% else %} 11 | Pipe Dream 12 | {% endif %} 13 | 14 | 15 | 16 | {% if site.meta_author %}{% endif %} 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /docs/_docs/dsl/pipeline.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Pipeline DSL 3 | nav_text: Pipeline 4 | categories: dsl 5 | nav_order: 10 6 | --- 7 | 8 | The pipeline DSL allows you to define the Stages and Actions within that Stage with only a few lines of code. In the [Quick Start]({% link quick-start.md %}), we define a very short pipeline to keep the introduction simple. Here we'll show more of the DSL power. 9 | 10 | ```ruby 11 | stage "Source" do 12 | github( 13 | source: "tongueroo/demo-test", 14 | auth_token: ssm("/github/user/token") 15 | ) 16 | end 17 | 18 | stage "Build" do 19 | codebuild "demo1", "demo2" 20 | codebuild "demo3" 21 | end 22 | 23 | stage "Approve" do 24 | approve("Approve this deploy") 25 | end 26 | 27 | stage "Deploy" do 28 | codebuild "deploy" 29 | end 30 | ``` 31 | 32 | This pipeline has 3 stages: 33 | 34 | 1. Downloads the source code from Gitub and uploads it to S3 as an output artifact. 35 | 2. Starts 3 codebuild projects with s3 upload from the previous step as an input artifact. 36 | 3. Waits for a manual approval stage. 37 | 4. Uses another codebuild project to kick off a deploy. 38 | 39 | ## Build Stage 40 | 41 | Within the build stage, there are multiple actions. Some of them run in parallel and some in serial. 42 | 43 | * The demo1 and demo2 codebuild projects run on the same `RunOrder=1`. They run in parallel. 44 | * The demo3 codebuild project run with `RunOrder=2`. It starts after both demo1 and demo2 finishes. 45 | 46 | The Pipeline DSL allows to you connect the stages together how you want them with very little code. 47 | 48 | ## Pipeline Specific DSL Docs 49 | 50 | {% assign docs = site.docs | where: "categories","dsl-pipeline" %} 51 | {% for doc in docs -%} 52 | * [{{doc.nav_text}}]({{doc.url}}) 53 | {% endfor %} 54 | 55 | {% include prev_next.md %} 56 | -------------------------------------------------------------------------------- /docs/_docs/conventions.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Naming Conventions 3 | nav_order: 8 4 | --- 5 | 6 | Pipe Dream follows a few naming conventions. 7 | 8 | ## Pipeline Name 9 | 10 | It will set the pipeline name by inferring the name of the parent folder. For example, if the parent folder is `demo`. 11 | 12 | cd demo 13 | pipe deploy 14 | 15 | The pipeline is named `demo`. You can override this easily by providing a pipeline name. 16 | 17 | pipe deploy my-pipeline # explicitly use my-pipeline as pipeline name 18 | 19 | The pipeline is named `my-pipeline` 20 | 21 | ## PIPE_ENV_EXTRA 22 | 23 | The `PIPE_ENV_EXTRA` also affects the name of the pipeline. It gets appended at the end of the pipeline name. 24 | 25 | PIPE_ENV_EXTRA=2 pipe deploy my-pipeline 26 | 27 | The pipeline is named `my-pipeline-2`. 28 | 29 | ## Settings append_env option 30 | 31 | If the append_env is configured in the [Settings]({% link _docs/settings.md %}), then the `PIPE_ENV` is added to the pipeline name. For example: `demo-development` instead of `demo`. 32 | 33 | ## Pipeline and Stack Names 34 | 35 | The CloudFormation stack name which creates the CodePipeline related resources is named the same as the pipeline name with `-pipe` appended to the stack name. Examples: 36 | 37 | PIPE_ENV | PIPE_ENV_EXTRA | append_env | Pipeline Name | Stack Name 38 | --- | --- | --- | --- | --- 39 | development | (not set) | false | demo | demo-pipe 40 | development | (not set) | true | demo-development | demo-development-pipe 41 | production | (not set) | true | demo-production | demo-production-pipe 42 | development | 2 | false | demo-2 | demo-2-pipe 43 | development | (not set) | true | demo-development | demo-development-pipe | 44 | development | 2 | true | demo-development | demo-development-2-pipe 45 | 46 | {% include prev_next.md %} 47 | -------------------------------------------------------------------------------- /docs/_sass/_global.scss: -------------------------------------------------------------------------------- 1 | // Global styling for this template 2 | html, 3 | body { 4 | width: 100%; 5 | height: 100%; 6 | } 7 | 8 | body { 9 | @include body-font; 10 | } 11 | 12 | a { 13 | color: #0275d8; 14 | @include transition-all; 15 | &:hover, 16 | &:focus { 17 | color: darken(#0275d8, 10%); 18 | } 19 | } 20 | 21 | hr { 22 | max-width: 100px; 23 | margin: 25px auto 0; 24 | border-width: 1px; 25 | border-color: fade-out($gray-darker, .9); 26 | } 27 | 28 | hr.light { 29 | border-color: white; 30 | } 31 | 32 | h1, 33 | h2, 34 | h3, 35 | h4, 36 | h5, 37 | h6 { 38 | @include heading-font; 39 | } 40 | 41 | p { 42 | font-size: 1em; 43 | line-height: 1.5; 44 | margin-bottom: 20px; 45 | @media (min-width:992px) { 46 | font-size: 1.1em; 47 | } 48 | } 49 | 50 | li { 51 | font-size: 16px; 52 | @media (min-width:992px) { 53 | font-size: 18px; 54 | } 55 | } 56 | 57 | // code blocks 58 | pre { 59 | font-size: 12px; 60 | } 61 | 62 | table td { 63 | vertical-align: top; 64 | } 65 | 66 | code { 67 | font-size: 80%; 68 | } 69 | 70 | 71 | section { 72 | padding: 60px 0; 73 | h2 { 74 | // font-size: 50px; 75 | } 76 | } 77 | 78 | ::-moz-selection { 79 | color: white; 80 | background: $gray-darker; 81 | text-shadow: none; 82 | } 83 | 84 | ::selection { 85 | color: white; 86 | background: $gray-darker; 87 | text-shadow: none; 88 | } 89 | 90 | img::selection { 91 | color: white; 92 | background: transparent; 93 | } 94 | 95 | img::-moz-selection { 96 | color: white; 97 | background: transparent; 98 | } 99 | 100 | body { 101 | -webkit-tap-highlight-color: $gray-darker; 102 | } 103 | -------------------------------------------------------------------------------- /pipedream.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path("../lib", __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require "pipedream/version" 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "pipedream" 8 | spec.version = Pipedream::VERSION 9 | spec.authors = ["Tung Nguyen"] 10 | spec.email = ["tongueroo@gmail.com"] 11 | spec.summary = "A beautiful and powerful DSL to create and manage AWS CodePipeline pipelines" 12 | spec.homepage = "https://github.com/tongueroo/pipedream" 13 | spec.license = "MIT" 14 | 15 | vendor_files = Dir.glob("vendor/**/*") 16 | gem_files = `git -C "#{File.dirname(__FILE__)}" ls-files -z`.split("\x0").reject do |f| 17 | f.match(%r{^(test|spec|features|docs)/}) 18 | end 19 | spec.files = gem_files + vendor_files 20 | spec.bindir = "exe" 21 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 22 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 23 | spec.require_paths = ["lib"] 24 | 25 | spec.add_dependency "activesupport" 26 | spec.add_dependency "aws-mfa-secure" 27 | spec.add_dependency "aws-sdk-cloudformation" 28 | spec.add_dependency "aws-sdk-codepipeline" 29 | spec.add_dependency "aws-sdk-s3" 30 | spec.add_dependency "aws-sdk-ssm" 31 | spec.add_dependency "cfn_camelizer" 32 | spec.add_dependency "memoist" 33 | spec.add_dependency "rainbow" 34 | spec.add_dependency "render_me_pretty" 35 | spec.add_dependency "rexml" 36 | spec.add_dependency "thor" 37 | spec.add_dependency "zeitwerk" 38 | 39 | spec.add_development_dependency "bundler" 40 | spec.add_development_dependency "byebug" 41 | spec.add_development_dependency "cli_markdown" 42 | spec.add_development_dependency "rake" 43 | spec.add_development_dependency "rspec" 44 | end 45 | -------------------------------------------------------------------------------- /docs/vendor/font-awesome/less/mixins.less: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | .fa-icon() { 5 | display: inline-block; 6 | font: normal normal normal @fa-font-size-base/@fa-line-height-base FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | 14 | .fa-icon-rotate(@degrees, @rotation) { 15 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation})"; 16 | -webkit-transform: rotate(@degrees); 17 | -ms-transform: rotate(@degrees); 18 | transform: rotate(@degrees); 19 | } 20 | 21 | .fa-icon-flip(@horiz, @vert, @rotation) { 22 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation}, mirror=1)"; 23 | -webkit-transform: scale(@horiz, @vert); 24 | -ms-transform: scale(@horiz, @vert); 25 | transform: scale(@horiz, @vert); 26 | } 27 | 28 | 29 | // Only display content to screen readers. A la Bootstrap 4. 30 | // 31 | // See: http://a11yproject.com/posts/how-to-hide-content/ 32 | 33 | .sr-only() { 34 | position: absolute; 35 | width: 1px; 36 | height: 1px; 37 | padding: 0; 38 | margin: -1px; 39 | overflow: hidden; 40 | clip: rect(0,0,0,0); 41 | border: 0; 42 | } 43 | 44 | // Use in conjunction with .sr-only to only display content when it's focused. 45 | // 46 | // Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 47 | // 48 | // Credit: HTML5 Boilerplate 49 | 50 | .sr-only-focusable() { 51 | &:active, 52 | &:focus { 53 | position: static; 54 | width: auto; 55 | height: auto; 56 | margin: 0; 57 | overflow: visible; 58 | clip: auto; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /docs/_docs/dsl/role.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Role DSL 3 | nav_text: Role 4 | categories: dsl 5 | nav_order: 14 6 | --- 7 | 8 | Pipe Dream can create the IAM service role associated with the pipeline. Here's an example: 9 | 10 | .pipedream/role.rb: 11 | 12 | ```ruby 13 | iam_policy("logs", "ssm") 14 | ``` 15 | 16 | For more control, here's a longer form: 17 | 18 | ```ruby 19 | iam_policy( 20 | action: [ 21 | "logs:CreateLogGroup", 22 | "logs:CreateLogStream", 23 | "logs:PutLogEvents", 24 | "ssm:*", 25 | ], 26 | effect: "Allow", 27 | resource: "*" 28 | ) 29 | ``` 30 | 31 | You can also create managed IAM policy. 32 | 33 | ```ruby 34 | managed_iam_policy("AmazonS3ReadOnlyAccess") 35 | ``` 36 | 37 | You can also add multiple managed IAM policies: 38 | 39 | ```ruby 40 | managed_iam_policy("AmazonS3ReadOnlyAccess", "AmazonEC2ReadOnlyAccess") 41 | ``` 42 | 43 | ## Full DSL 44 | 45 | The convenience methods merely wrap properties of the [AWS::IAM::Role 46 | CloudFormation Resource](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html). If you wanted to set the CloudFormation properties more directly, here's an example of using the "Full" DSL. 47 | 48 | .pipedream/role.rb: 49 | 50 | ```ruby 51 | assume_role_policy_document( 52 | statement: [{ 53 | action: ["sts:AssumeRole"], 54 | effect: "Allow", 55 | principal: { 56 | service: ["codepipeline.amazonaws.com"] 57 | } 58 | }], 59 | version: "2012-10-17" 60 | ) 61 | path("/") 62 | policies([{ 63 | policy_name: "CodeBuildAccess", 64 | policy_document: { 65 | version: "2012-10-17", 66 | statement: [{ 67 | action: [ 68 | "logs:CreateLogGroup", 69 | "logs:CreateLogStream", 70 | "logs:PutLogEvents", 71 | ], 72 | effect: "Allow", 73 | resource: "*" 74 | }] 75 | } 76 | }]) 77 | ``` 78 | 79 | {% include prev_next.md %} 80 | -------------------------------------------------------------------------------- /docs/_docs/examples/multiple-codebuild-projects.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Multiple CodeBuild Projects 3 | nav_text: Multiple CodeBuild 4 | categories: examples 5 | nav_order: 20 6 | --- 7 | 8 | In this example guide, we'll create a couple of test CodeBuild projects and quickly connect them up to a pipeline. 9 | 10 | ## CodeBuild Projects 11 | 12 | We'll use the the [cody](https://cody.run/) tool to quickly get going. 13 | 14 | You can use any project, even an empty folder. You just have to make sure it is pushed to GitHub and has a `buildspec.yml`. If you need an example project, you can try this one: [tongueroo/demo-ufo](https://github.com/tongueroo/demo-ufo). 15 | 16 | First, you can use `cody init` create some starter `.cody` files. 17 | 18 | gem install cody # installs cody command 19 | cody init # create starter .cody files including buildspec.yml 20 | git add . 21 | git commit -m 'add starter .cody files' 22 | git push 23 | 24 | Then create the 4 CodeBuild projects for testing: 25 | 26 | for i in {1..4} ; do cody deploy demo$i --no-wait ; done 27 | 28 | ## CodePipeline 29 | 30 | Let's define a pipeline now with the 4 CodeBuild test projects. First, use `pipe init` to create the starter `.pipedream` files. Update your `pipeline.rb` with the following: 31 | 32 | .pipedream/pipeline.rb: 33 | 34 | ```ruby 35 | stage "Source" do 36 | github( 37 | source: "tongueroo/demo-ufo", # replace with your repo 38 | auth_token: ssm("/github/user/token") 39 | ) 40 | end 41 | 42 | stage "Build" do 43 | codebuild "demo1" 44 | codebuild "demo2", "demo3" # runs in parallel 45 | codebuild "demo4" 46 | end 47 | ``` 48 | 49 | Deploy it with: 50 | 51 | pipe deploy 52 | 53 | Last, start the pipeline execution: 54 | 55 | pipe start 56 | 57 | That's it! The pipeline will look like this: 58 | 59 | ![](/img/docs/multiple-codebuild-projects-pipeline.png) 60 | 61 | {% include prev_next.md %} 62 | -------------------------------------------------------------------------------- /docs/vendor/font-awesome/scss/_mixins.scss: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | @mixin fa-icon() { 5 | display: inline-block; 6 | font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | 14 | @mixin fa-icon-rotate($degrees, $rotation) { 15 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation})"; 16 | -webkit-transform: rotate($degrees); 17 | -ms-transform: rotate($degrees); 18 | transform: rotate($degrees); 19 | } 20 | 21 | @mixin fa-icon-flip($horiz, $vert, $rotation) { 22 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}, mirror=1)"; 23 | -webkit-transform: scale($horiz, $vert); 24 | -ms-transform: scale($horiz, $vert); 25 | transform: scale($horiz, $vert); 26 | } 27 | 28 | 29 | // Only display content to screen readers. A la Bootstrap 4. 30 | // 31 | // See: http://a11yproject.com/posts/how-to-hide-content/ 32 | 33 | @mixin sr-only { 34 | position: absolute; 35 | width: 1px; 36 | height: 1px; 37 | padding: 0; 38 | margin: -1px; 39 | overflow: hidden; 40 | clip: rect(0,0,0,0); 41 | border: 0; 42 | } 43 | 44 | // Use in conjunction with .sr-only to only display content when it's focused. 45 | // 46 | // Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 47 | // 48 | // Credit: HTML5 Boilerplate 49 | 50 | @mixin sr-only-focusable { 51 | &:active, 52 | &:focus { 53 | position: static; 54 | width: auto; 55 | height: auto; 56 | margin: 0; 57 | overflow: visible; 58 | clip: auto; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /docs/vendor/jquery-easing/jquery.easing.compatibility.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Easing Compatibility v1 - http://gsgd.co.uk/sandbox/jquery/easing 3 | * 4 | * Adds compatibility for applications that use the pre 1.2 easing names 5 | * 6 | * Copyright (c) 2007 George Smith 7 | * Licensed under the MIT License: 8 | * http://www.opensource.org/licenses/mit-license.php 9 | */ 10 | 11 | (function($){ 12 | $.extend( $.easing, 13 | { 14 | easeIn: function (x, t, b, c, d) { 15 | return $.easing.easeInQuad(x, t, b, c, d); 16 | }, 17 | easeOut: function (x, t, b, c, d) { 18 | return $.easing.easeOutQuad(x, t, b, c, d); 19 | }, 20 | easeInOut: function (x, t, b, c, d) { 21 | return $.easing.easeInOutQuad(x, t, b, c, d); 22 | }, 23 | expoin: function(x, t, b, c, d) { 24 | return $.easing.easeInExpo(x, t, b, c, d); 25 | }, 26 | expoout: function(x, t, b, c, d) { 27 | return $.easing.easeOutExpo(x, t, b, c, d); 28 | }, 29 | expoinout: function(x, t, b, c, d) { 30 | return $.easing.easeInOutExpo(x, t, b, c, d); 31 | }, 32 | bouncein: function(x, t, b, c, d) { 33 | return $.easing.easeInBounce(x, t, b, c, d); 34 | }, 35 | bounceout: function(x, t, b, c, d) { 36 | return $.easing.easeOutBounce(x, t, b, c, d); 37 | }, 38 | bounceinout: function(x, t, b, c, d) { 39 | return $.easing.easeInOutBounce(x, t, b, c, d); 40 | }, 41 | elasin: function(x, t, b, c, d) { 42 | return $.easing.easeInElastic(x, t, b, c, d); 43 | }, 44 | elasout: function(x, t, b, c, d) { 45 | return $.easing.easeOutElastic(x, t, b, c, d); 46 | }, 47 | elasinout: function(x, t, b, c, d) { 48 | return $.easing.easeInOutElastic(x, t, b, c, d); 49 | }, 50 | backin: function(x, t, b, c, d) { 51 | return $.easing.easeInBack(x, t, b, c, d); 52 | }, 53 | backout: function(x, t, b, c, d) { 54 | return $.easing.easeOutBack(x, t, b, c, d); 55 | }, 56 | backinout: function(x, t, b, c, d) { 57 | return $.easing.easeInOutBack(x, t, b, c, d); 58 | } 59 | });})(jQuery); 60 | -------------------------------------------------------------------------------- /docs/_docs/ecs-deploy.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'ECS Deploy: Codebuild ufo ship vs CodePipeline ECS Deploy' 3 | nav_order: 21 4 | --- 5 | 6 | CodePipeline comes with many [Action Type Integrations](https://docs.aws.amazon.com/codepipeline/latest/userguide/integrations-action-type.html). One of the Integrations is [Amazon Elastic Container Service](https://docs.aws.amazon.com/codepipeline/latest/userguide/integrations-action-type.html#integrations-deploy) deployment. It is recommended to use [codebuild and ufo](https://codebuild.cloud/docs/examples/ecs/) to handle deployment to ECS though. We discuss some reasons below. 7 | 8 | ## Timeout 9 | 10 | With the CodePipeline ECS Deploy if your ECS service fails to stabilize then the pipeline stage will not timeout until 60 minutes later. There is no built-in way to abort the pipeline stage. This is discussed here: [CodePipeline stage timeouts / abort?](https://forums.aws.amazon.com/thread.jspa?threadID=216350). 11 | 12 | A workaround is discussed here: [How to stop an execution or set set timeout for an action in AWS CodePipeline?](https://stackoverflow.com/questions/50925732/how-to-stop-an-execution-or-set-set-timeout-for-an-action-in-aws-codepipeline/50929558) So to workaround waiting for 60 minutes, we can update the pipeline. This is a little bit inconvenient. By using a CodeBuild project, we have control over the timeout. 13 | 14 | ## It's Less Powerful 15 | 16 | The way the current CodePipeline ECS Deploy Action works is that it pulls down the current ECS task definition of the ECS service. It then replaces the image property on it. Last, it then updates the ECS service with the newly built Docker image. 17 | 18 | The [ufo tool](https://ufoships.com) is more powerful. The `ufo ship` command also handles creating the [ELB Load Balancer]((https://ufoships.com/docs/extras/load-balancer/)) and a vanity [Route53 endpoint](https://ufoships.com/docs/extras/route53-support/) for us. Also, it keeps task definitions codified. 19 | 20 | {% include prev_next.md %} 21 | -------------------------------------------------------------------------------- /docs/_includes/subnav.html: -------------------------------------------------------------------------------- 1 |
2 | 47 |
48 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | # Site settings 2 | title: pipedream 3 | email: tongueroo@gmail.com 4 | url: https://pipedream.run 5 | description: "Pipedream CodePipeline DSL" 6 | keywords: "pipedream codepipeline boltops dsl ruby aws" 7 | skills: "" 8 | meta_author: Tung Nguyen 9 | 10 | # Google webmaster tools 11 | google_verify: 12 | google_analytics: "UA-98684555-12" 13 | 14 | # https://ssl.bing.com/webmaster/configure/verify/ownership Option 2 content= goes here 15 | bing_verify: 16 | 17 | # Contact form: 18 | # - static : pass through formspree.io to validate email sending 19 | # - disqus : replace contact form by disqus thread 20 | # - comment the line below if you want to stick with the default PHP contact form 21 | contact: static 22 | 23 | # If you use disqus you need disqus shortname 24 | # https://help.disqus.com/customer/portal/articles/466208 25 | disqus_shortname: 26 | 27 | # Color settings (hex-codes without the leading hash-tag) 28 | color: 29 | primary: 3972c7 30 | primary-rgb: "24,288,156" #"128,179,255" 31 | secondary: 2c3e50 #FD6E8A 32 | secondary-dark: 233140 #A2122F 33 | links: a3c8ff 34 | 35 | # Footer settings 36 | footer: 37 | copyright: BoltOps, LLC 38 | location: San Francisco, CA 39 | social: BoltOps 40 | credits: 41 | contact: contact@boltops.com 42 | phone: 43 | 44 | # Social networks usernames (many more available: google-plus, flickr, dribbble, pinterest, instagram, tumblr, linkedin, etc.) 45 | social: 46 | - title: twitter 47 | url: http://twitter.com/tongueroo 48 | - title: github 49 | url: https://github.com/tongueroo/pipedream 50 | 51 | # Credits content 52 | credits: 53 | 54 | # Build settings 55 | markdown: kramdown 56 | permalink: pretty 57 | 58 | gh_url: https://github.com/tongueroo/pipedream 59 | 60 | collections: 61 | docs: 62 | name: "Documentation" 63 | output: true 64 | reference: 65 | name: "Reference" 66 | output: true 67 | 68 | defaults: 69 | - values: 70 | layout: default 71 | 72 | plugins_dir: 73 | - jekyll-coffeescript 74 | -------------------------------------------------------------------------------- /lib/pipedream/sequence.rb: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | require 'thor' 3 | 4 | module Pipedream 5 | class Sequence < Thor::Group 6 | include AwsServices 7 | include Thor::Actions 8 | 9 | add_runtime_options! # force, pretend, quiet, skip options 10 | # https://github.com/erikhuda/thor/blob/master/lib/thor/actions.rb#L49 11 | 12 | def self.source_paths 13 | [File.expand_path("../../template", __FILE__)] 14 | end 15 | 16 | private 17 | def override_source_paths(*paths) 18 | # Using string with instance_eval because block doesnt have access to 19 | # path at runtime. 20 | self.class.instance_eval %{ 21 | def self.source_paths 22 | #{paths.flatten.inspect} 23 | end 24 | } 25 | end 26 | 27 | def sync_template_repo 28 | unless git_installed? 29 | abort "Unable to detect git installation on your system. Git needs to be installed in order to use the --template option." 30 | end 31 | 32 | template_path = "#{ENV['HOME']}/.pipedream/templates/#{full_repo_name}" 33 | if File.exist?(template_path) 34 | sh("cd #{template_path} && git pull") 35 | else 36 | FileUtils.mkdir_p(File.dirname(template_path)) 37 | sh("git clone #{repo_url} #{template_path}") 38 | end 39 | end 40 | 41 | def full_repo_name 42 | full_repo = options[:template].split("/")[-2..-1].join("/") 43 | full_repo = full_repo.split(":").last 44 | full_repo.sub(".git", "") 45 | end 46 | 47 | # normalize repo_url 48 | def repo_url 49 | template = options[:template] 50 | if template.include?('github.com') 51 | template # leave as is, user has provided full github url 52 | else 53 | "https://github.com/#{template}" 54 | end 55 | end 56 | 57 | def git_installed? 58 | system("type git > /dev/null") 59 | end 60 | 61 | def sh(command) 62 | puts "=> #{command}" 63 | system(command) 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /lib/pipedream/core.rb: -------------------------------------------------------------------------------- 1 | require 'pathname' 2 | require 'yaml' 3 | require 'active_support/core_ext/string' 4 | 5 | module Pipedream 6 | module Core 7 | extend Memoist 8 | 9 | def root 10 | path = ENV['PIPE_ROOT'] || '.' 11 | Pathname.new(path) 12 | end 13 | 14 | def env 15 | # 2-way binding 16 | pipe_env = env_from_profile || 'development' 17 | pipe_env = ENV['PIPE_ENV'] if ENV['PIPE_ENV'] # highest precedence 18 | ActiveSupport::StringInquirer.new(pipe_env) 19 | end 20 | memoize :env 21 | 22 | def env_extra 23 | env_extra = ENV['PIPE_ENV_EXTRA'] if ENV['PIPE_ENV_EXTRA'] # highest precedence 24 | return if env_extra&.empty? 25 | env_extra 26 | end 27 | memoize :env_extra 28 | 29 | # Overrides AWS_PROFILE based on the Pipedream.env if set in configs/settings.yml 30 | # 2-way binding. 31 | def set_aws_profile! 32 | return if ENV['TEST'] 33 | return unless File.exist?("#{Pipedream.root}/.pipedream/settings.yml") # for rake docs 34 | return unless settings # Only load if within Pipedream project and there's a settings.yml 35 | 36 | data = settings || {} 37 | if data[:aws_profile] 38 | puts "Using AWS_PROFILE=#{data[:aws_profile]} from PIPE_ENV=#{Pipedream.env} in config/settings.yml" 39 | ENV['AWS_PROFILE'] = data[:aws_profile] 40 | end 41 | end 42 | 43 | def settings 44 | Setting.new.data 45 | end 46 | memoize :settings 47 | 48 | def check_pipedream_project! 49 | check_path = "#{Pipedream.root}/.pipedream" 50 | unless File.exist?(check_path) 51 | puts "ERROR: No .pipedream folder found. Are you sure you are in a project with pipedream setup?".color(:red) 52 | puts "Current directory: #{Dir.pwd}" 53 | puts "If you want to set up pipedream for this prjoect, please create a settings file via: pipe init" 54 | exit 1 unless ENV['TEST'] 55 | end 56 | end 57 | 58 | private 59 | def env_from_profile 60 | Pipedream::Setting.new.pipe_env 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /docs/_includes/footer.html: -------------------------------------------------------------------------------- 1 | 35 | 36 | 43 | -------------------------------------------------------------------------------- /docs/_sass/_mixins.scss: -------------------------------------------------------------------------------- 1 | // Mixins 2 | @mixin transition-all() { 3 | -webkit-transition: all .35s; 4 | -moz-transition: all .35s; 5 | transition: all .35s; 6 | } 7 | 8 | @mixin background-cover() { 9 | -webkit-background-size: cover; 10 | -moz-background-size: cover; 11 | -o-background-size: cover; 12 | background-size: cover; 13 | } 14 | 15 | @mixin button-variant($color, $background, $border) { 16 | color: $color; 17 | border-color: $border; 18 | background-color: $background; 19 | &:focus, 20 | &.focus { 21 | color: $color; 22 | border-color: darken($border, 25%); 23 | background-color: darken($background, 10%); 24 | } 25 | &:hover { 26 | color: $color; 27 | border-color: darken($border, 12%); 28 | background-color: darken($background, 10%); 29 | } 30 | &:active, 31 | &.active, 32 | .open > &.dropdown-toggle { 33 | color: $color; 34 | border-color: darken($border, 12%); 35 | background-color: darken($background, 10%); 36 | &:hover, 37 | &:focus, 38 | &.focus { 39 | color: $color; 40 | border-color: darken($border, 25%); 41 | background-color: darken($background, 17%); 42 | } 43 | } 44 | &:active, 45 | &.active, 46 | .open > &.dropdown-toggle { 47 | background-image: none; 48 | } 49 | &.disabled, 50 | &[disabled], 51 | fieldset[disabled] & { 52 | &:hover, 53 | &:focus, 54 | &.focus { 55 | border-color: $border; 56 | background-color: $background; 57 | } 58 | } 59 | .badge { 60 | color: $background; 61 | background-color: $color; 62 | } 63 | } 64 | 65 | @mixin heading-font { 66 | font-family: 'Catamaran', 'Helvetica', 'Arial', 'sans-serif'; 67 | font-weight: 200; 68 | letter-spacing: 1px; 69 | } 70 | 71 | @mixin body-font { 72 | font-family: 'Muli', 'Helvetica', 'Arial', 'sans-serif'; 73 | } 74 | 75 | @mixin alt-font { 76 | font-family: 'Lato', 'Helvetica', 'Arial', 'sans-serif'; 77 | letter-spacing: 2px; 78 | text-transform: uppercase; 79 | } 80 | -------------------------------------------------------------------------------- /lib/pipedream/cli.rb: -------------------------------------------------------------------------------- 1 | module Pipedream 2 | class CLI < Command 3 | class_option :verbose, type: :boolean 4 | class_option :noop, type: :boolean 5 | 6 | desc "init", "Initialize project with .codepipline files" 7 | long_desc Help.text(:init) 8 | Init.cli_options.each do |args| 9 | option(*args) 10 | end 11 | register(Init, "init", "init", "Set up initial .codepipline files.") 12 | 13 | common_options = Proc.new do 14 | option :stack_name, desc: "Override the generated stack name. If you use this you must always specify it" 15 | option :wait, type: :boolean, default: true, desc: "Wait for operation to complete" 16 | end 17 | 18 | desc "deploy PIPELINE_NAME", "Deploy pipeline." 19 | long_desc Help.text(:deploy) 20 | option :branch, aliases: "b", desc: "git branch" # important to default to nil 21 | common_options.call 22 | def deploy(pipeline_name=nil) 23 | Deploy.new(options.merge(pipeline_name: pipeline_name)).run 24 | end 25 | 26 | desc "start", "Start codebuild project." 27 | long_desc Help.text(:start) 28 | option :sure, desc: "Bypass are you sure prompt" 29 | option :branch, aliases: "b", desc: "git branch" # important to default to nil 30 | common_options.call 31 | def start(pipeline_name=nil) 32 | Start.new(options.merge(pipeline_name: pipeline_name)).run 33 | end 34 | 35 | desc "delete", "Delete codebuild project." 36 | long_desc Help.text(:delete) 37 | option :sure, desc: "Bypass are you sure prompt" 38 | common_options.call 39 | def delete(pipeline_name=nil) 40 | Delete.new(options.merge(pipeline_name: pipeline_name)).run 41 | end 42 | 43 | desc "completion *PARAMS", "Prints words for auto-completion." 44 | long_desc Help.text("completion") 45 | def completion(*params) 46 | Completer.new(CLI, *params).run 47 | end 48 | 49 | desc "completion_script", "Generates a script that can be eval to setup auto-completion." 50 | long_desc Help.text("completion_script") 51 | def completion_script 52 | Completer::Script.generate 53 | end 54 | 55 | desc "version", "prints version" 56 | def version 57 | puts VERSION 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /docs/_sass/_masthead.scss: -------------------------------------------------------------------------------- 1 | // Styling for the masthead 2 | header.masthead { 3 | position: relative; 4 | overflow-y: hidden; 5 | width: 100%; 6 | min-height: auto; 7 | color: white; 8 | background-color: #020766; 9 | // background: url('../img/bg-pattern.png'), $theme-secondary; 10 | // background: url('../img/bg-pattern.png'), -webkit-linear-gradient(to left, $theme-secondary, $theme-tertiary); 11 | // background: url('../img/bg-pattern.png'), linear-gradient(to left, $theme-secondary, $theme-tertiary); 12 | .header-content { 13 | position: relative; 14 | padding: 100px 0 50px; 15 | text-align: center; 16 | .header-content-inner { 17 | position: relative; 18 | max-width: 500px; 19 | margin: 0 auto; 20 | h1 { 21 | font-size: 30px; 22 | margin-top: 0; 23 | margin-bottom: 30px; 24 | } 25 | .list-badges { 26 | margin-bottom: 25px; 27 | img { 28 | height: 50px; 29 | margin-bottom: 25px; 30 | } 31 | } 32 | } 33 | } 34 | .device-container { 35 | max-width: 300px; 36 | margin: 0 auto 100px; 37 | .screen img { 38 | border-radius: 3px; 39 | } 40 | } 41 | @media (min-width: 768px) { 42 | min-height: 100%; 43 | .header-content { 44 | height: 100vh; 45 | min-height: 600px; 46 | padding: 0; 47 | text-align: left; 48 | .header-content-inner { 49 | position: absolute; 50 | top: 50%; 51 | max-width: none; 52 | margin: 0; 53 | transform: translateY(-50%); 54 | h1 { 55 | font-size: 35px; 56 | } 57 | } 58 | } 59 | .device-container { 60 | max-width: none; 61 | max-height: calc(100vh - 100px); 62 | margin: 100px auto 0; 63 | } 64 | } 65 | @media (min-width: 992px) { 66 | .header-content .header-content-inner h1 { 67 | font-size: 50px; 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/pipedream/init.rb: -------------------------------------------------------------------------------- 1 | module Pipedream 2 | class Init < Sequence 3 | # Ugly, this is how I can get the options from to match with this Thor::Group 4 | def self.cli_options 5 | [ 6 | [:name, desc: "CodePipeline project name."], 7 | [:mode, desc: "Modes: light or full", default: "light" ], 8 | [:force, type: :boolean, desc: "Bypass overwrite are you sure prompt for existing files."], 9 | [:template, desc: "Custom template to use."], 10 | [:template_mode, desc: "Template mode: replace or additive."], 11 | ] 12 | end 13 | cli_options.each { |o| class_option(*o) } 14 | 15 | def setup_template_repo 16 | return unless @options[:template]&.include?('/') 17 | 18 | sync_template_repo 19 | end 20 | 21 | def set_source_path 22 | return unless @options[:template] 23 | 24 | custom_template = "#{ENV['HOME']}/.pipedream/templates/#{full_repo_name}" 25 | 26 | if @options[:template_mode] == "replace" # replace the template entirely 27 | override_source_paths(custom_template) 28 | else # additive: modify on top of default template 29 | default_template = File.expand_path("../../template", __FILE__) 30 | override_source_paths([custom_template, default_template]) 31 | end 32 | end 33 | 34 | def copy_project 35 | puts "Initialize pipedream project in .pipedream" 36 | 37 | excludes = %w[.git] 38 | if @options[:mode] == "light" 39 | excludes += %w[ 40 | settings.yml 41 | sns.rb 42 | ] 43 | end 44 | pattern = Regexp.new(excludes.join('|')) 45 | 46 | if @options[:template] 47 | directory ".", ".pipedream", exclude_pattern: pattern 48 | else 49 | directory ".", exclude_pattern: pattern 50 | end 51 | end 52 | 53 | private 54 | def project_name 55 | inferred_name = File.basename(Dir.pwd).gsub('_','-').gsub(/[^0-9a-zA-Z,-]/, '') 56 | @options[:name] || inferred_name 57 | end 58 | 59 | def project_github_repo 60 | default = "user/repo" 61 | return default unless File.exist?(".git/config") && git_installed? 62 | 63 | url = `git config --get remote.origin.url`.strip 64 | repo = Dsl::Pipeline::Github.extract_repo_source(url) 65 | repo == '' ? default : repo 66 | end 67 | end 68 | end -------------------------------------------------------------------------------- /docs/_docs/dsl/pipeline/approve.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Approval Action DSL 3 | nav_text: Approve 4 | categories: dsl-pipeline 5 | nav_order: 12 6 | --- 7 | 8 | You can add an Approval Action to a Stage with the `approve` method. There are various helpful forms. Let's start with the simplest form. 9 | 10 | ## String Form 11 | 12 | The approve method take can take a simple String. In this form, it sets the message for the approval action. Essentially, it sets the `configuration.CustomData` property of the Approval action. See [Add an Action to a Pipeline in CodePipeline](https://docs.aws.amazon.com/codepipeline/latest/userguide/approvals-action-add.html) docs for the full structure. 13 | 14 | ```ruby 15 | stage "Approve" do 16 | approve("Approve this deployment") 17 | end 18 | ``` 19 | 20 | With CodePipeline, an SNS topic is required to be associated with the Approval Action. In the case of a String form, pipedream will automatically create and manage the SNS topic associated with the `approve` declaration. 21 | 22 | ## Simplified Configuration Hash Form 23 | 24 | If the `approve` method is provided a Hash with the `notification_arn` and `custom_data`, then pipedream will set the `configuration` directly. Example: 25 | 26 | ```ruby 27 | stage "Approve" do 28 | approve( 29 | notification_arn: "arn:aws:sns:us-west-2:112233445566:hello-topic", 30 | custom_data: "Approve deployment", 31 | ) 32 | end 33 | ``` 34 | 35 | In this case, the Pipe Dream will *not* create an SNS Topic as we're have specified an existing SNS topic. 36 | 37 | ## Full Config Form 38 | 39 | The convenience methods merely wrap a CodePipeline Approval Action. An example of the Approval Action structure is provided in the [Add an Action to a Pipeline in CodePipeline](https://docs.aws.amazon.com/codepipeline/latest/userguide/approvals-action-add.html) docs. 40 | 41 | If you need to set the properties more directly, here's an example of using the "Full" Config. 42 | 43 | ```ruby 44 | stage "Approve" do 45 | approve( 46 | name: "approve", 47 | action_type_id: { 48 | category: "Approval", 49 | owner: "AWS", 50 | provider: "Manual", 51 | version: "1", 52 | }, 53 | run_order: 1, 54 | configuration: { 55 | custom_data: "my message", 56 | notification_arn: {ref: "SnsTopic"}, # defaults to generated SNS topic 57 | }, 58 | ) 59 | end 60 | ``` 61 | 62 | {% include prev_next.md %} 63 | -------------------------------------------------------------------------------- /docs/_docs/dsl/pipeline/codebuild.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Codebuild Project 3 | nav_text: Codebuild 4 | categories: dsl-pipeline 5 | nav_order: 13 6 | --- 7 | 8 | The `codebuild` method is one of the most useful methods in the `pipeline.rb` DSL arsenal. With it, you can add codebuild projects to your pipeline and sequence them quickly. Let's start with the simplest form. 9 | 10 | ## String Form 11 | 12 | With the String form, the String sets both the name of the CodePipeline Action and CodeBuild Project. 13 | 14 | ```ruby 15 | stage "Deploy" do 16 | codebuild "demo1" 17 | codebuild "demo2", "demo3" # runs in parallel 18 | codebuild "demo4" 19 | end 20 | ``` 21 | 22 | With just a few lines of code, we have define the pipeline to run the `demo1` codebuild project first. Then, in parallel, run `demo2` and `demo3` second. Last, run `demo4`. 23 | 24 | ## Simplified Hash Form 25 | 26 | With the Simplified Hash form, we have more control over the CodePipeline Action and CodeBuild Project names. 27 | 28 | ```ruby 29 | stage "Deploy" do 30 | codebuild(name: "action1", project_name: "demo1") 31 | codebuild({name: "action2", project_name: "demo2"}, {name: "action3", project_name: "demo3"}) # runs in parallel 32 | codebuild(name: "action4", project_name: "demo4") 33 | end 34 | ``` 35 | 36 | In this form, we can explicitly set what the Action Name displays as in the CodePipeline. We can also set the CodeBuild project name explicitly. 37 | 38 | Note: You can mix and match the String form and the Hash form. Each item in the argument list gets evaluated and expanded out appropriately. 39 | 40 | ## Full Hash Form 41 | 42 | With the Full Hash Form, we have full control of the Action properties. To keep this example concise, we'll declare only one codebuild Action. 43 | 44 | ```ruby 45 | stage "Build" do 46 | codebuild( 47 | name: "action1", 48 | action_type_id: { 49 | category: "Build", 50 | owner: "AWS", 51 | provider: "CodeBuild", 52 | version: "1", 53 | }, 54 | run_order: 1, 55 | configuration: { project_name: 'demo1' }, 56 | # output_artifacts: [name: "BuildArtifact#{name}"], # optional 57 | input_artifacts: [name: "SourceArtifact"], # default 58 | ) 59 | end 60 | ``` 61 | 62 | The various forms all ultimately expand properties to the Full Hash Form. Generally, it is recommended you start with the simplest form and use the more complex forms when required. 63 | 64 | {% include prev_next.md %} 65 | -------------------------------------------------------------------------------- /lib/pipedream/pipeline/s3_bucket.rb: -------------------------------------------------------------------------------- 1 | require "aws-sdk-s3" 2 | 3 | class Pipedream::Pipeline 4 | class S3Bucket 5 | extend Memoist 6 | include Pipedream::AwsServices 7 | 8 | class << self 9 | extend Memoist 10 | def name 11 | new.name 12 | end 13 | memoize :name 14 | end 15 | 16 | def name 17 | ensure_exists(bucket_name) 18 | bucket_name 19 | end 20 | memoize :name 21 | 22 | def bucket_name 23 | "codepipeline-#{aws.region}-#{aws.account}" 24 | end 25 | 26 | def ensure_exists(name) 27 | return if exists?(name) || ENV['TEST'] 28 | s3.create_bucket(bucket: name) 29 | policy = { 30 | "Version": "2012-10-17", 31 | "Id": "SSEAndSSLPolicy", 32 | "Statement": [ 33 | { 34 | "Sid": "DenyUnEncryptedObjectUploads", 35 | "Effect": "Deny", 36 | "Principal": "*", 37 | "Action": "s3:PutObject", 38 | "Resource": "arn:aws:s3:::#{name}/*", 39 | "Condition": { 40 | "StringNotEquals": { 41 | "s3:x-amz-server-side-encryption": "aws:kms" 42 | } 43 | } 44 | }, 45 | { 46 | "Sid": "DenyInsecureConnections", 47 | "Effect": "Deny", 48 | "Principal": "*", 49 | "Action": "s3:*", 50 | "Resource": "arn:aws:s3:::#{name}/*", 51 | "Condition": { 52 | "Bool": { 53 | "aws:SecureTransport": "false" 54 | } 55 | } 56 | } 57 | ] 58 | } 59 | s3.put_bucket_policy( 60 | bucket: name, 61 | policy: JSON.dump(policy), 62 | ) 63 | rescue Aws::S3::Errors::BucketAlreadyExists => e 64 | puts "ERROR #{e.class}: #{e.message}".color(:red) 65 | puts "Bucket name: #{name}" 66 | exit 1 67 | end 68 | 69 | def exists?(name) 70 | begin 71 | s3.head_bucket(bucket: name) 72 | true 73 | rescue Aws::S3::Errors::BucketAlreadyOwnedByYou, Aws::S3::Errors::Http301Error 74 | # These exceptions indicate bucket already exists 75 | # Aws::S3::Errors::Http301Error could be inaccurate but compromising for simplicity 76 | true 77 | rescue 78 | false 79 | end 80 | end 81 | 82 | private 83 | def aws 84 | AwsData.new 85 | end 86 | memoize :aws 87 | end 88 | end 89 | -------------------------------------------------------------------------------- /docs/vendor/jquery-easing/jquery.easing.min.js: -------------------------------------------------------------------------------- 1 | (function(factory){if(typeof define==="function"&&define.amd){define(["jquery"],function($){return factory($)})}else if(typeof module==="object"&&typeof module.exports==="object"){exports=factory(require("jquery"))}else{factory(jQuery)}})(function($){$.easing.jswing=$.easing.swing;var pow=Math.pow,sqrt=Math.sqrt,sin=Math.sin,cos=Math.cos,PI=Math.PI,c1=1.70158,c2=c1*1.525,c3=c1+1,c4=2*PI/3,c5=2*PI/4.5;function bounceOut(x){var n1=7.5625,d1=2.75;if(x<1/d1){return n1*x*x}else if(x<2/d1){return n1*(x-=1.5/d1)*x+.75}else if(x<2.5/d1){return n1*(x-=2.25/d1)*x+.9375}else{return n1*(x-=2.625/d1)*x+.984375}}$.extend($.easing,{def:"easeOutQuad",swing:function(x){return $.easing[$.easing.def](x)},easeInQuad:function(x){return x*x},easeOutQuad:function(x){return 1-(1-x)*(1-x)},easeInOutQuad:function(x){return x<.5?2*x*x:1-pow(-2*x+2,2)/2},easeInCubic:function(x){return x*x*x},easeOutCubic:function(x){return 1-pow(1-x,3)},easeInOutCubic:function(x){return x<.5?4*x*x*x:1-pow(-2*x+2,3)/2},easeInQuart:function(x){return x*x*x*x},easeOutQuart:function(x){return 1-pow(1-x,4)},easeInOutQuart:function(x){return x<.5?8*x*x*x*x:1-pow(-2*x+2,4)/2},easeInQuint:function(x){return x*x*x*x*x},easeOutQuint:function(x){return 1-pow(1-x,5)},easeInOutQuint:function(x){return x<.5?16*x*x*x*x*x:1-pow(-2*x+2,5)/2},easeInSine:function(x){return 1-cos(x*PI/2)},easeOutSine:function(x){return sin(x*PI/2)},easeInOutSine:function(x){return-(cos(PI*x)-1)/2},easeInExpo:function(x){return x===0?0:pow(2,10*x-10)},easeOutExpo:function(x){return x===1?1:1-pow(2,-10*x)},easeInOutExpo:function(x){return x===0?0:x===1?1:x<.5?pow(2,20*x-10)/2:(2-pow(2,-20*x+10))/2},easeInCirc:function(x){return 1-sqrt(1-pow(x,2))},easeOutCirc:function(x){return sqrt(1-pow(x-1,2))},easeInOutCirc:function(x){return x<.5?(1-sqrt(1-pow(2*x,2)))/2:(sqrt(1-pow(-2*x+2,2))+1)/2},easeInElastic:function(x){return x===0?0:x===1?1:-pow(2,10*x-10)*sin((x*10-10.75)*c4)},easeOutElastic:function(x){return x===0?0:x===1?1:pow(2,-10*x)*sin((x*10-.75)*c4)+1},easeInOutElastic:function(x){return x===0?0:x===1?1:x<.5?-(pow(2,20*x-10)*sin((20*x-11.125)*c5))/2:pow(2,-20*x+10)*sin((20*x-11.125)*c5)/2+1},easeInBack:function(x){return c3*x*x*x-c1*x*x},easeOutBack:function(x){return 1+c3*pow(x-1,3)+c1*pow(x-1,2)},easeInOutBack:function(x){return x<.5?pow(2*x,2)*((c2+1)*2*x-c2)/2:(pow(2*x-2,2)*((c2+1)*(x*2-2)+c2)+2)/2},easeInBounce:function(x){return 1-bounceOut(1-x)},easeOutBounce:bounceOut,easeInOutBounce:function(x){return x<.5?(1-bounceOut(1-2*x))/2:(1+bounceOut(2*x-1))/2}})}); -------------------------------------------------------------------------------- /lib/pipedream/command.rb: -------------------------------------------------------------------------------- 1 | require "thor" 2 | 3 | # Override thor's long_desc identation behavior 4 | # https://github.com/erikhuda/thor/issues/398 5 | class Thor 6 | module Shell 7 | class Basic 8 | def print_wrapped(message, options = {}) 9 | message = "\n#{message}" unless message[0] == "\n" 10 | stdout.puts message 11 | end 12 | end 13 | end 14 | end 15 | 16 | module Pipedream 17 | class Command < Thor 18 | class << self 19 | def dispatch(m, args, options, config) 20 | # Allow calling for help via: 21 | # codepipe command help 22 | # codepipe command -h 23 | # codepipe command --help 24 | # codepipe command -D 25 | # 26 | # as well thor's normal way: 27 | # 28 | # codepipe help command 29 | help_flags = Thor::HELP_MAPPINGS + ["help"] 30 | if args.length > 1 && !(args & help_flags).empty? 31 | args -= help_flags 32 | args.insert(-2, "help") 33 | end 34 | 35 | # codepipe version 36 | # codepipe --version 37 | # codepipe -v 38 | version_flags = ["--version", "-v"] 39 | if args.length == 1 && !(args & version_flags).empty? 40 | args = ["version"] 41 | end 42 | 43 | super 44 | end 45 | 46 | # Override command_help to include the description at the top of the 47 | # long_description. 48 | def command_help(shell, command_name) 49 | meth = normalize_command_name(command_name) 50 | command = all_commands[meth] 51 | alter_command_description(command) 52 | super 53 | end 54 | 55 | def alter_command_description(command) 56 | return unless command 57 | 58 | # Add description to beginning of long_description 59 | long_desc = if command.long_description 60 | "#{command.description}\n\n#{command.long_description}" 61 | else 62 | command.description 63 | end 64 | 65 | # add reference url to end of the long_description 66 | unless website.empty? 67 | full_command = [command.ancestor_name, command.name].compact.join('-') 68 | url = "#{website}/reference/codepipe-#{full_command}" 69 | long_desc += "\n\nHelp also available at: #{url}" 70 | end 71 | 72 | command.long_description = long_desc 73 | end 74 | private :alter_command_description 75 | 76 | # meant to be overriden 77 | def website 78 | "" 79 | end 80 | end 81 | end 82 | end 83 | -------------------------------------------------------------------------------- /docs/vendor/bootstrap/css/bootstrap-reboot.min.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../../scss/_normalize.scss","bootstrap-reboot.css","../../scss/_reboot.scss","../../scss/_variables.scss","../../scss/mixins/_hover.scss"],"names":[],"mappings":"4EAYA,KACE,YAAA,WACA,YAAA,KACA,qBAAA,KACA,yBAAA,KAUF,KACE,OAAA,EAOF,QAAA,MAAA,OAAA,OAAA,IAAA,QAME,QAAA,MAQF,GACE,UAAA,IACA,OAAA,MAAA,EAWF,WAAA,OAAA,KAGE,QAAA,MAOF,OACE,OAAA,IAAA,KAQF,GACE,mBAAA,YAAA,WAAA,YACA,OAAA,EACA,SAAA,QAQF,IACE,YAAA,UAAA,UACA,UAAA,IAWF,EACE,iBAAA,YACA,6BAAA,QAQF,SAAA,QAEE,cAAA,EAQF,YACE,cAAA,KACA,gBAAA,UACA,gBAAA,UAAA,OAOF,EAAA,OAEE,YAAA,QAOF,EAAA,OAEE,YAAA,OAQF,KAAA,IAAA,KAGE,YAAA,UAAA,UACA,UAAA,IAOF,IACE,WAAA,OAOF,KACE,iBAAA,KACA,MAAA,KAOF,MACE,UAAA,IAQF,IAAA,IAEE,UAAA,IACA,YAAA,EACA,SAAA,SACA,eAAA,SAGF,IACE,OAAA,OAGF,IACE,IAAA,MAUF,MAAA,MAEE,QAAA,aAOF,sBACE,QAAA,KACA,OAAA,EAOF,IACE,aAAA,KAOF,eACE,SAAA,OAWF,OAAA,MAAA,SAAA,OAAA,SAKE,YAAA,WACA,UAAA,KACA,YAAA,KACA,OAAA,EAQF,OAAA,MAEE,SAAA,QAQF,OAAA,OAEE,eAAA,KASF,aAAA,cAAA,OAAA,mBAIE,mBAAA,OAOF,gCAAA,+BAAA,gCAAA,yBAIE,aAAA,KACA,QAAA,EAOF,6BAAA,4BAAA,6BAAA,sBAIE,QAAA,IAAA,OAAA,WAOF,SACE,OAAA,IAAA,MAAA,OACA,OAAA,EAAA,IACA,QAAA,MAAA,OAAA,MAUF,OACE,mBAAA,WAAA,WAAA,WACA,MAAA,QACA,QAAA,MACA,UAAA,KACA,QAAA,EACA,YAAA,OAQF,SACE,QAAA,aACA,eAAA,SAOF,SACE,SAAA,KCrKF,gBAAA,aD+KE,mBAAA,WAAA,WAAA,WACA,QAAA,EC1KF,yCAAA,yCDmLE,OAAA,KC9KF,cDuLE,mBAAA,UACA,eAAA,KCnLF,4CAAA,yCD4LE,mBAAA,KAQF,6BACE,mBAAA,OACA,KAAA,QAWF,QAAA,KAEE,QAAA,MAOF,QACE,QAAA,UAUF,OACE,QAAA,aAOF,SACE,QAAA,KCnNF,SD8NE,QAAA,KEtbF,KACE,mBAAA,WAAA,WAAA,WAGF,EAAA,QAAA,SAGE,mBAAA,QAAA,WAAA,QAoBA,cAAgB,MAAA,aAQlB,KAYE,mBAAA,UAGA,4BAAA,YAGF,KACE,YAAA,cAAA,UAAA,mBAAA,WAAA,OC2K4H,iBD3K5H,MAAA,WACA,UAAA,KACA,YAAA,IACA,YAAA,IAEA,MAAA,QAEA,iBAAA,KD2LF,sBClLE,QAAA,YAYF,GAAI,GAAI,GAAI,GAAI,GAAI,GAClB,WAAA,EACA,cAAA,MAOF,EACE,WAAA,EACA,cAAA,KAIF,0BAAA,YAGE,OAAA,KAGF,QACE,cAAA,KACA,WAAA,OACA,YAAA,QAGF,GAAA,GAAA,GAGE,WAAA,EACA,cAAA,KAGF,MAAA,MAAA,MAAA,MAIE,cAAA,EAGF,GACE,YAAA,IAGF,GACE,cAAA,MACA,YAAA,EAGF,WACE,OAAA,EAAA,EAAA,KAQF,EACE,MAAA,QACA,gBAAA,KEhJE,QAAA,QFmJA,MAAA,QACA,gBAAA,UAUJ,8BACE,MAAA,QACA,gBAAA,KEhKE,oCAAA,oCFmKA,MAAA,QACA,gBAAA,KANJ,oCAUI,QAAA,EASJ,IAEE,WAAA,EAEA,cAAA,KAEA,SAAA,KAQF,OAGE,OAAA,EAAA,EAAA,KAQF,IAGE,eAAA,ODsIF,cCzHE,OAAA,QAcF,cAAA,EAAA,KAAA,OAAA,MAAA,MAAA,OAAA,QAAA,SASE,iBAAA,aAAA,aAAA,aAQF,MAEE,gBAAA,SAEA,iBAAA,YAGF,QACE,YAAA,OACA,eAAA,OACA,MAAA,QACA,WAAA,KACA,aAAA,OAGF,GAEE,WAAA,KAQF,MAEE,QAAA,aACA,cAAA,MAOF,aACE,QAAA,IAAA,OACA,QAAA,IAAA,KAAA,yBAGF,OAAA,MAAA,OAAA,SAME,YAAA,QAGF,8BAAA,2BAMI,OAAA,YAKJ,iBAAA,iBAAA,2BAAA,kBASE,mBAAA,QAGF,SAEE,OAAA,SAGF,SAME,UAAA,EAEA,QAAA,EACA,OAAA,EACA,OAAA,EAGF,OAEE,QAAA,MACA,MAAA,KACA,QAAA,EACA,cAAA,MACA,UAAA,OACA,YAAA,QAGF,mBAKE,mBAAA,KAIF,OACE,QAAA,aDsEF,SC9DE,QAAA"} -------------------------------------------------------------------------------- /docs/quick-start.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Quick Start 3 | nav_order: 1 4 | --- 5 | 6 | In a hurry? No problem! Here's a quick start to get going. 7 | 8 | ## Summary 9 | 10 | gem install pipedream 11 | cd 12 | pipe init # generates starter .pipedream files 13 | # edit .pipedream/pipeline.rb 14 | pipe deploy # create the CodePipeline pipeline via CloudFormation 15 | pipe start # start a CodePipeline pipeline execution 16 | 17 | ## What Happened? 18 | 19 | Here are a little more details on what the summarized commands do. First, we install the pipedream tool. 20 | 21 | gem install pipedream 22 | 23 | Change into your project directory. 24 | 25 | cd 26 | 27 | If you need a demo project, you can try this demo project: [tongueroo/demo-ufo](git clone https://github.com/tongueroo/demo-ufo). 28 | 29 | git clone https://github.com/tongueroo/demo-ufo demo 30 | cd demo 31 | 32 | Create the starter .pipedream files in the project. 33 | 34 | pipe init # generates starter .pipedream files 35 | 36 | An important generated file is `.pipedream/pipeline.rb`. The starter file defines the pipeline via an [CodePipeline DSL]({% link _docs/dsl.md %}). It looks something like this: 37 | 38 | ```ruby 39 | stage "Source" do 40 | github( 41 | source: "user/repo", # replace with your repo 42 | auth_token: ssm("/github/user/token") # replace with your token 43 | ) 44 | end 45 | 46 | stage "Build" do 47 | codebuild "demo" 48 | end 49 | ``` 50 | 51 | The pipeline definition is much shorter than typical CloudFormation code. In this short pipeline, there are 2 stages: 52 | 53 | 1. Downloads the source code from Gitub and uploads it to S3 as an output artifact. 54 | 2. Starts some codebuild project with the code that was previously uploaded to s3 as the input artifact. 55 | 56 | Note, you need to have a codebuild project already created as a prerequisite. The example instructions for that are here: [Create CodeBuild Project]({% link _docs/examples/codebuild-project.md %}). 57 | 58 | You can then deploy or create the pipeline with a single command: 59 | 60 | pipe deploy 61 | 62 | This deploys a CloudFormation stack that creates a CodePipeline pipeline and IAM role. The IAM role permissions is defined in `.pipedream/role.rb` via the [IAM Role DSL]({% link _docs/dsl/role.md %}). 63 | 64 | Once the stack is complete. You can start the CodePipeline pipeline via the CLI or the CodePipeline console. Here is the CLI command: 65 | 66 | pipe start 67 | 68 | Here's what CodePipeline pipeline output looks like: 69 | 70 | ![](/img/docs/codepipeline-output.png) 71 | 72 | {% include prev_next.md %} 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | # Pipe Dream 6 | 7 | ![Build Status](https://codebuild.us-west-2.amazonaws.com/badges?uuid=eyJlbmNyeXB0ZWREYXRhIjoiWk1FM0dldzE5MUM5R3VqVGxxTmRFb1JGNnkxQjJpTDYvajYrQk91YzErNjdNc1VYVElHM3V5ZEJXcStyMmZVc210WG8vUURSV2JST0ZpSWc5Y0pYR3k0PSIsIml2UGFyYW1ldGVyU3BlYyI6IldvYXhLMU8yS2pQdVRKbEoiLCJtYXRlcmlhbFNldFNlcmlhbCI6MX0%3D&branch=master) 8 | [![Gem Version](https://badge.fury.io/rb/pipedream.png)](http://badge.fury.io/rb/pipedream) 9 | 10 | [![BoltOps Badge](https://img.boltops.com/boltops/badges/boltops-badge.png)](https://www.boltops.com) 11 | 12 | Pipe Dream provides a DSL to make it easy create a CodePipeline pipeline. 13 | 14 | Pipe Dream installs `pipedream` and `pipe` executables. Both of them do the same thing, `pipe` is just shorter to type. 15 | 16 | The documentation site is at: [pipedream.run](https://pipedream.run/) 17 | 18 | ## Quick Start 19 | 20 | pipe init 21 | pipe deploy 22 | pipe start 23 | pipe delete 24 | 25 | ## Init and Structure 26 | 27 | First, run `pipe init` to generate a starter `.pipedream` folder structure. 28 | 29 | $ tree .pipedream 30 | .pipedream 31 | ├── pipeline.rb 32 | └── schedule.rb 33 | 34 | File | Description 35 | --- | --- 36 | pipeline.rb | The CodePipeline pipeline written as a DSL. This is required. Here are the [Pipeline DSL docs](https://pipedream.run/docs/dsl/pipeline/) 37 | schedule.rb | A CloudWatch scheduled event written as a DSL. Here are the [Schedule DSL docs](https://pipedream.run/docs/dsl/schedule/) 38 | 39 | ## DSL 40 | 41 | .pipedream/pipeline.rb: 42 | 43 | ```ruby 44 | stage "Source" do 45 | github( 46 | source: "tongueroo/demo-ufo", 47 | auth_token: ssm("/github/user/token") 48 | ) 49 | end 50 | stage "DeployStacks" do 51 | codebuild "demo1" # action declaration 52 | codebuild "demo2", "demo3" # will run in parallel 53 | codebuild "demo4" # action declaration 54 | end 55 | ``` 56 | 57 | More [DSL docs](https://pipedream.run/docs/dsl/) 58 | 59 | ## Installation 60 | 61 | Add this line to your application's Gemfile: 62 | 63 | gem "pipedream" 64 | 65 | And then execute: 66 | 67 | bundle 68 | 69 | Or install it yourself as: 70 | 71 | gem install pipedream 72 | 73 | ## Contributing 74 | 75 | 1. Fork it 76 | 2. Create your feature branch (`git checkout -b my-new-feature`) 77 | 3. Commit your changes (`git commit -am "Add some feature"`) 78 | 4. Push to the branch (`git push origin my-new-feature`) 79 | 5. Create new Pull Request 80 | -------------------------------------------------------------------------------- /docs/_sass/_navbar.scss: -------------------------------------------------------------------------------- 1 | // Styling for the navbar 2 | #mainNav { 3 | border-color: fade-out($gray-darker, .95); 4 | background-color: white; 5 | color: #777; 6 | 7 | border-bottom-color: #efefef; 8 | border-bottom-width: 2px; 9 | border-bottom-style: solid; 10 | 11 | @include transition-all; 12 | @include heading-font; 13 | .navbar-brand { 14 | color: #222; 15 | @include heading-font; 16 | &:hover, 17 | &:focus { 18 | color: darken($theme-primary, 10%); 19 | } 20 | @media (min-width: 992px) { 21 | font-size: 2em; 22 | } 23 | } 24 | .navbar-toggler { 25 | font-size: 12px; 26 | padding: 8px 10px; 27 | color: $gray-darker; 28 | } 29 | .navbar-nav { 30 | > li { 31 | > a { 32 | font-size: 1.05em; 33 | @include alt-font; 34 | &.active { 35 | color: $theme-primary !important; 36 | background-color: transparent; 37 | &:hover { 38 | background-color: transparent; 39 | } 40 | } 41 | } 42 | > a, 43 | > a:focus { 44 | color: $gray-darker; 45 | &:hover { 46 | color: $theme-primary; 47 | } 48 | } 49 | } 50 | } 51 | @media (min-width: 992px) { 52 | border-bottom-color: #efefef; 53 | border-bottom-width: 2px; 54 | border-bottom-style: solid; 55 | 56 | background-color: transparent; 57 | background-color: white; 58 | 59 | .navbar-brand { 60 | color: fade(#777, 70%); 61 | &:hover, 62 | &:focus { 63 | color: #777; 64 | } 65 | } 66 | .navbar-nav > li > a, 67 | .navbar-nav > li > a:focus { 68 | // color: fade-out(#777, .3); 69 | &:hover { 70 | color: #777; 71 | } 72 | } 73 | &.navbar-shrink { 74 | border-color: fade-out($gray-darker, .9); 75 | background-color: white; 76 | .navbar-brand { 77 | color: $gray-darker; 78 | &:hover, 79 | &:focus { 80 | color: $theme-primary; 81 | } 82 | } 83 | .navbar-nav > li > a, 84 | .navbar-nav > li > a:focus { 85 | color: $gray-darker; 86 | &:hover { 87 | color: $theme-primary; 88 | } 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /lib/pipedream/help/deploy.md: -------------------------------------------------------------------------------- 1 | Examples: 2 | 3 | pipe deploy 4 | pipe deploy demo # explicitly specify pipeline name 5 | pipe deploy demo -b mybranch # specify git branch 6 | 7 | The pipeline is generated from the DSL and created with CloudFormation. The files that the DSL evaluates are in the `.pipedream` folder: 8 | 9 | .pipedream/pipeline.rb 10 | .pipedream/role.rb 11 | .pipedream/schedule.rb 12 | .pipedream/webhook.rb 13 | 14 | To create the CodePipeline pipeline, you run: 15 | 16 | pipe deploy 17 | 18 | You'll see output that looks something like this: 19 | 20 | $ pipe deploy 21 | Generated CloudFormation template at /tmp/codepipeline.yml 22 | Deploying stack demo-pipe with CodePipeline project demo 23 | Creating stack demo-pipe. Check CloudFormation console for status. 24 | Stack name demo-pipe status CREATE_IN_PROGRESS 25 | Here's the CloudFormation url to check for more details https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks 26 | Waiting for stack to complete 27 | 04:14:03AM CREATE_IN_PROGRESS AWS::CloudFormation::Stack demo-pipe User Initiated 28 | 04:14:06AM CREATE_IN_PROGRESS AWS::IAM::Role IamRole 29 | 04:14:07AM CREATE_IN_PROGRESS AWS::IAM::Role IamRole Resource creation Initiated 30 | 04:14:25AM CREATE_COMPLETE AWS::IAM::Role IamRole 31 | 04:14:28AM CREATE_IN_PROGRESS AWS::CodePipeline::Pipeline Pipeline 32 | 04:14:29AM CREATE_IN_PROGRESS AWS::CodePipeline::Pipeline Pipeline Resource creation Initiated 33 | 04:14:29AM CREATE_COMPLETE AWS::CodePipeline::Pipeline Pipeline 34 | 04:14:31AM CREATE_IN_PROGRESS AWS::CodePipeline::Webhook Webhook 35 | 04:14:33AM CREATE_IN_PROGRESS AWS::CodePipeline::Webhook Webhook Resource creation Initiated 36 | 04:14:33AM CREATE_COMPLETE AWS::CodePipeline::Webhook Webhook 37 | 04:14:35AM CREATE_COMPLETE AWS::CloudFormation::Stack demo-pipe 38 | Stack success status: CREATE_COMPLETE 39 | Time took for stack deployment: 35s. 40 | $ 41 | 42 | ## Explicit Pipeline Name 43 | 44 | By default, the pipeline name is inferred and is the parent folder that you are within. You can explicitly specify the pipeline name as the first CLI argument: 45 | 46 | pipe deploy my-pipeline 47 | 48 | ## Specify Git Branch 49 | 50 | It is useful to build pipelines with different source git branches. You can pass a `--branch` option to the `pipe deploy` command. The cli `—-branch` option always takes the highest precedence. Example: 51 | 52 | pipe deploy my-pipeline --branch my-branch 53 | 54 | Note: When you specify a branch pipedream actually first updates the pipeline before starting the pipeline execution. This is done because CodePipeline does not natively support specifying the branch. It is discussed more here: [Using Different Branches]({% link _docs/examples/different-branches.md %}). 55 | -------------------------------------------------------------------------------- /docs/_docs/deploy.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Deploy 3 | nav_order: 5 4 | --- 5 | 6 | The pipeline is generated from the DSL and created with CloudFormation. The files that the DSL evaluates are in the `.pipedream` folder: 7 | 8 | .pipedream/pipeline.rb 9 | .pipedream/role.rb 10 | .pipedream/schedule.rb 11 | .pipedream/webhook.rb 12 | 13 | To create the CodePipeline pipeline, you run: 14 | 15 | pipe deploy 16 | 17 | You'll see output that looks something like this: 18 | 19 | $ pipe deploy 20 | Generated CloudFormation template at /tmp/codepipeline.yml 21 | Deploying stack demo-pipe with CodePipeline project demo 22 | Creating stack demo-pipe. Check CloudFormation console for status. 23 | Stack name demo-pipe status CREATE_IN_PROGRESS 24 | Here's the CloudFormation url to check for more details https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks 25 | Waiting for stack to complete 26 | 04:14:03AM CREATE_IN_PROGRESS AWS::CloudFormation::Stack demo-pipe User Initiated 27 | 04:14:06AM CREATE_IN_PROGRESS AWS::IAM::Role IamRole 28 | 04:14:07AM CREATE_IN_PROGRESS AWS::IAM::Role IamRole Resource creation Initiated 29 | 04:14:25AM CREATE_COMPLETE AWS::IAM::Role IamRole 30 | 04:14:28AM CREATE_IN_PROGRESS AWS::CodePipeline::Pipeline Pipeline 31 | 04:14:29AM CREATE_IN_PROGRESS AWS::CodePipeline::Pipeline Pipeline Resource creation Initiated 32 | 04:14:29AM CREATE_COMPLETE AWS::CodePipeline::Pipeline Pipeline 33 | 04:14:31AM CREATE_IN_PROGRESS AWS::CodePipeline::Webhook Webhook 34 | 04:14:33AM CREATE_IN_PROGRESS AWS::CodePipeline::Webhook Webhook Resource creation Initiated 35 | 04:14:33AM CREATE_COMPLETE AWS::CodePipeline::Webhook Webhook 36 | 04:14:35AM CREATE_COMPLETE AWS::CloudFormation::Stack demo-pipe 37 | Stack success status: CREATE_COMPLETE 38 | Time took for stack deployment: 35s. 39 | $ 40 | 41 | ## Explicit Pipeline Name 42 | 43 | By default, the pipeline name is inferred and is the parent folder name that you are within. You can explicitly specify the pipeline name as the first CLI argument: 44 | 45 | pipe deploy my-pipeline 46 | 47 | ## Specify Git Branch 48 | 49 | It is useful to build pipelines with different source git branches. You can pass a `--branch` option to the `pipe deploy` command. The cli `—-branch` option always takes the highest precedence. Example: 50 | 51 | pipe deploy my-pipeline --branch my-branch 52 | 53 | Note: When you specify a branch pipedream actually first updates the pipeline before starting the pipeline execution. This is done because CodePipeline does not currently support specifying the branch. It is discussed more here: [Using Different Branches]({% link _docs/examples/different-branches.md %}). 54 | 55 | ## CLI Reference 56 | 57 | Also, for help info you can check the [pipe deploy]({% link _reference/pipe-deploy.md %}) CLI reference. 58 | 59 | {% include prev_next.md %} 60 | -------------------------------------------------------------------------------- /lib/pipedream/aws_services/helpers.rb: -------------------------------------------------------------------------------- 1 | module Pipedream::AwsServices 2 | module Helpers 3 | def stack_exists?(stack_name) 4 | return false if ENV['TEST'] 5 | 6 | exist = nil 7 | begin 8 | # When the stack does not exist an exception is raised. Example: 9 | # Aws::CloudFormation::Errors::ValidationError: Stack with id blah does not exist 10 | cfn.describe_stacks(stack_name: stack_name) 11 | exist = true 12 | rescue Aws::CloudFormation::Errors::ValidationError => e 13 | if e.message =~ /does not exist/ 14 | exist = false 15 | elsif e.message.include?("'stackName' failed to satisfy constraint") 16 | # Example of e.message when describe_stack with invalid stack name 17 | # "1 validation error detected: Value 'instance_and_route53' at 'stackName' failed to satisfy constraint: Member must satisfy regular expression pattern: [a-zA-Z][-a-zA-Z0-9]*|arn:[-a-zA-Z0-9:/._+]*" 18 | puts "Invalid stack name: #{stack_name}" 19 | puts "Full error message: #{e.message}" 20 | exit 1 21 | else 22 | raise # re-raise exception because unsure what other errors can happen 23 | end 24 | end 25 | exist 26 | end 27 | 28 | def pipeline_name_convention(name_base) 29 | items = [@pipeline_name, @options[:type], Pipedream.env_extra] 30 | items.insert(2, Pipedream.env) if Pipedream.settings.dig(:stack_naming, :append_env) 31 | items.reject(&:blank?).compact.join("-") 32 | end 33 | 34 | def inferred_pipeline_name 35 | # Essentially the project's parent folder 36 | File.basename(Dir.pwd).gsub('_','-').gsub(/\.+/,'-').gsub(/[^0-9a-zA-Z,-]/, '') 37 | end 38 | 39 | # Examples: 40 | # 41 | # myapp-ci-deploy # with Settings stack_naming append_env set to false. 42 | # myapp-ci-deploy-development 43 | # myapp-ci-deploy-development-2 44 | # 45 | def inferred_stack_name(pipeline_name) 46 | items = [pipeline_name, @options[:type], Pipedream.env_extra, "pipe"] 47 | append_env = Pipedream.settings.dig(:stack_naming, :append_env) 48 | items.insert(2, Pipedream.env) if append_env 49 | items.reject(&:blank?).compact.join("-") 50 | end 51 | 52 | def are_you_sure?(stack_name, action) 53 | if @options[:sure] 54 | sure = 'y' 55 | else 56 | message = case action 57 | when :update 58 | "Are you sure you want to want to update the #{stack_name.color(:green)} stack with the changes? (y/N)" 59 | when :delete 60 | "Are you sure you want to want to delete the #{stack_name.color(:green)} stack? (y/N)" 61 | end 62 | puts message 63 | sure = $stdin.gets 64 | end 65 | 66 | unless sure =~ /^y/ 67 | puts "Whew! Exiting without running #{action}." 68 | exit 0 69 | end 70 | end 71 | end 72 | end -------------------------------------------------------------------------------- /lib/pipedream/setting.rb: -------------------------------------------------------------------------------- 1 | require 'yaml' 2 | require 'render_me_pretty' 3 | 4 | module Pipedream 5 | class Setting 6 | extend Memoist 7 | def initialize(check_pipedream_project=true) 8 | @check_pipedream_project = check_pipedream_project 9 | end 10 | 11 | # data contains the settings.yml config. The order or precedence for settings 12 | # is the project ufo/settings.yml and then the ~/.pipedream/settings.yml. 13 | def data 14 | Pipedream.check_pipedream_project! if @check_pipedream_project 15 | return {} unless File.exist?(project_settings_path) 16 | 17 | # project based settings files 18 | project = load_file(project_settings_path) 19 | 20 | user_file = "#{ENV['HOME']}/.pipedream/settings.yml" 21 | user = File.exist?(user_file) ? YAML.load_file(user_file) : {} 22 | 23 | default_file = File.expand_path("default/settings.yml", __dir__) 24 | default = load_file(default_file) 25 | 26 | all_envs = default.deep_merge(user.deep_merge(project)) 27 | all_envs = merge_base(all_envs) 28 | 29 | env_data = all_envs[pipe_env] || {} 30 | base_data = all_envs["base"] || {} 31 | data = base_data.merge(env_data) 32 | 33 | data.deep_symbolize_keys 34 | end 35 | memoize :data 36 | 37 | # Resolves infinite problem since Pipedream.env can be determined from PIPE_ENV or settings.yml files. 38 | # When ufo is determined from settings it should not called Pipedream.env since that in turn calls 39 | # Settings.new.data which can then cause an infinite loop. 40 | def pipe_env 41 | path = "#{cb_root}/.pipedream/settings.yml" 42 | if File.exist?(path) 43 | settings = YAML.load_file(path) 44 | env = settings.find do |_env, section| 45 | section ||= {} 46 | ENV['AWS_PROFILE'] && ENV['AWS_PROFILE'] == section['aws_profile'] 47 | end 48 | end 49 | 50 | pipe_env = env.first if env 51 | pipe_env = ENV['PIPE_ENV'] if ENV['PIPE_ENV'] # highest precedence 52 | pipe_env || 'development' 53 | end 54 | 55 | private 56 | def load_file(path) 57 | return Hash.new({}) unless File.exist?(path) 58 | 59 | content = RenderMePretty.result(path) 60 | data = YAML.load(content) 61 | # If key is is accidentally set to nil it screws up the merge_base later. 62 | # So ensure that all keys with nil value are set to {} 63 | data.each do |env, _setting| 64 | data[env] ||= {} 65 | end 66 | data 67 | end 68 | 69 | # automatically add base settings to the rest of the environments 70 | def merge_base(all_envs) 71 | base = all_envs["base"] || {} 72 | all_envs.each do |env, settings| 73 | all_envs[env] = base.merge(settings) unless env == "base" 74 | end 75 | all_envs 76 | end 77 | 78 | def project_settings_path 79 | "#{cb_root}/.pipedream/settings.yml" 80 | end 81 | 82 | def cb_root 83 | ENV["PIPE_ROOT"] || Dir.pwd 84 | end 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /lib/pipedream/schedule.rb: -------------------------------------------------------------------------------- 1 | module Pipedream 2 | class Schedule 3 | include Pipedream::Dsl::Schedule 4 | include Evaluate 5 | 6 | def initialize(options={}) 7 | @options = options 8 | @schedule_path = options[:schedule_path] || get_schedule_path 9 | @properties = default_properties 10 | end 11 | 12 | def run 13 | return unless File.exist?(@schedule_path) 14 | 15 | old_properties = @properties.clone 16 | evaluate(@schedule_path) 17 | 18 | @properties[:schedule_expression] = @schedule_expression if @schedule_expression 19 | set_rule_event! if @rule_event_props 20 | return if old_properties == @properties # empty schedule.rb file 21 | 22 | resource = { 23 | events_rule: { 24 | type: "AWS::Events::Rule", 25 | properties: @properties 26 | }, 27 | events_rule_role: events_rule_role, 28 | } 29 | CfnCamelizer.transform(resource) 30 | end 31 | 32 | def set_rule_event! 33 | props = @rule_event_props 34 | if props.key?(:detail) 35 | description = props.key?(:description) ? props.delete(:description) : rule_description 36 | rule_props = { event_pattern: props, description: description } 37 | else # if props.key?(:event_pattern) 38 | props[:description] ||= rule_description 39 | rule_props = props 40 | end 41 | 42 | @properties.merge!(rule_props) 43 | end 44 | 45 | def default_properties 46 | description = "CodePipeline #{@options[:full_pipeline_name]}" 47 | name = description.gsub(" ", "-").downcase 48 | { 49 | description: description, 50 | # event_pattern: , 51 | name: name, 52 | # schedule_expression: , 53 | state: "ENABLED", 54 | targets: [{ 55 | arn: "arn:aws:codepipeline:#{aws.region}:#{aws.account}:#{@options[:full_pipeline_name]}", 56 | role_arn: { "Fn::GetAtt": "EventsRuleRole.Arn" }, # required for specific CodePipeline target. 57 | id: "CodePipelineTarget", 58 | }] 59 | } 60 | end 61 | 62 | private 63 | def get_schedule_path 64 | lookup_pipedream_file("schedule.rb") 65 | end 66 | 67 | def events_rule_role 68 | { 69 | type: "AWS::IAM::Role", 70 | properties: { 71 | assume_role_policy_document: { 72 | statement: [{ 73 | action: [ "sts:AssumeRole" ], 74 | effect: "Allow", 75 | principal: { service: [ "events.amazonaws.com" ] } 76 | }], 77 | version: "2012-10-17" 78 | }, 79 | path: "/", 80 | policies: [{ 81 | policy_name: "CodePipelineAccess", 82 | policy_document: { 83 | version: "2012-10-17", 84 | statement: [{ 85 | action: "codepipeline:StartPipelineExecution", 86 | effect: "Allow", 87 | resource: "arn:aws:codepipeline:#{aws.region}:#{aws.account}:#{@options[:full_pipeline_name]}" 88 | }] 89 | } 90 | }] 91 | } 92 | } 93 | end 94 | 95 | def aws 96 | @aws ||= AwsData.new 97 | end 98 | end 99 | end 100 | -------------------------------------------------------------------------------- /lib/pipedream/start.rb: -------------------------------------------------------------------------------- 1 | module Pipedream 2 | class Start 3 | extend Memoist 4 | include AwsServices 5 | 6 | def initialize(options) 7 | @options = options 8 | @pipeline_name = options[:pipeline_name] || inferred_pipeline_name 9 | @full_pipeline_name = pipeline_name_convention(@pipeline_name) 10 | @stack_name = options[:stack_name] || inferred_stack_name(@pipeline_name) 11 | end 12 | 13 | def run 14 | check_pipeline_exists! 15 | redeploy 16 | resp = codepipeline.start_pipeline_execution(name: pipeline_name) 17 | codepipeline_info(resp.pipeline_execution_id) 18 | end 19 | 20 | # Pipedreamline does not currently support specifying a different branch starting an execution. 21 | # Workaround this limitation by updating the pipeline and then starting the execution. 22 | def redeploy 23 | return unless different_branch? 24 | puts "Different branch detected." 25 | puts " Current pipeline branch: #{current_pipeline_branch}" 26 | puts " Requested branch: #{@options[:branch]}" 27 | puts "Updating pipeline with new branch.".color(:green) 28 | Deploy.new(@options).run 29 | end 30 | 31 | def different_branch? 32 | return false unless @options[:branch] 33 | current_pipeline_branch != @options[:branch] 34 | end 35 | 36 | # Actual branch on current pipeline 37 | def current_pipeline_branch 38 | resp = codepipeline.get_pipeline(name: pipeline_name) 39 | source_stage = resp.pipeline.stages.find { |s| s.name == "Source" } 40 | action = source_stage.actions.first 41 | action.configuration['Branch'] 42 | end 43 | memoize :current_pipeline_branch 44 | 45 | def check_pipeline_exists! 46 | pipeline_name 47 | end 48 | 49 | def pipeline_name 50 | if pipeline_exists?(@full_pipeline_name) 51 | @full_pipeline_name 52 | elsif stack_exists?(@stack_name) # allow `cb start STACK_NAME` to work too 53 | resp = cfn.describe_stack_resources(stack_name: @stack_name) 54 | resource = resp.stack_resources.find do |r| 55 | r.logical_resource_id == "CodePipeline" 56 | end 57 | resource.physical_resource_id # pipeline name 58 | else 59 | puts "ERROR: Unable to find the pipeline with either full_pipeline_name: #{@full_pipeline_name} or stack name: #{@stack_name}".color(:red) 60 | exit 1 61 | end 62 | end 63 | memoize :pipeline_name 64 | 65 | private 66 | def codepipeline_info(execution_id) 67 | region = `aws configure get region`.strip rescue "us-east-1" 68 | url = "https://#{region}.console.aws.amazon.com/codesuite/codepipeline/pipelines/#{pipeline_name}/view" 69 | 70 | puts "Pipeline started: #{pipeline_name}" 71 | puts "Please check the CodePipeline console for the status." 72 | puts "CodePipeline Console: #{url}" 73 | puts "Pipeline cli commands:" 74 | puts 75 | puts " aws codepipeline get-pipeline-execution --pipeline-execution-id #{execution_id} --pipeline-name #{pipeline_name}" 76 | puts " aws codepipeline get-pipeline-state --name #{pipeline_name}" 77 | puts 78 | end 79 | 80 | def pipeline_exists?(name) 81 | codepipeline.get_pipeline(name: name) 82 | true 83 | rescue Aws::CodePipeline::Errors::PipelineNotFoundException 84 | false 85 | end 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /docs/_includes/commands.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

The Code

5 |

Easy to learn

6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |

Overview

14 |
15 | {% highlight sh %} 16 | pipe init 17 | pipe deploy 18 | pipe start 19 | pipe delete 20 | {% endhighlight %} 21 |
22 |
23 |
24 |

Structure

25 |
26 | {% highlight sh %} 27 | .pipedream 28 | ├── pipeline.rb 29 | ├── role.rb 30 | ├── schedule.rb 31 | ├── settings.yml 32 | └── webhook.rb 33 | {% endhighlight %} 34 |
35 |
36 |
37 |
38 |
39 |

Usage

40 |
41 | {% highlight sh %} 42 | pipe deploy # infers the pipeline name from parent folder 43 | pipe deploy pipeline-name # explicitly specify pipeline name 44 | 45 | pipe start # infers the pipeline name from parent folder 46 | pipe start pipeline-name # explicitly specify pipeline name 47 | {% endhighlight %} 48 |
49 |
50 |
51 |

pipeline.rb

52 |
53 | {% highlight ruby %} 54 | stage "Source" do 55 | github( 56 | source: "user/repo", 57 | auth_token: ssm("/github/user/token") 58 | ) 59 | end 60 | stage "DeployStacks" do 61 | codebuild "demo1" # action declaration 62 | codebuild "demo2", "demo3" # will run in parallel 63 | codebuild "demo4" # action declaration 64 | end 65 | {% endhighlight %} 66 |
67 |
68 |
69 |
70 |
71 |

role.rb

72 |
73 | 74 | {% highlight ruby %} 75 | iam_policy("logs", "ssm") 76 | {% endhighlight %} 77 |
78 |
79 |
80 |

webhook.rb

81 |
82 | {% highlight ruby %} 83 | github_token(ssm("/codepipeline/github/token")) 84 | {% endhighlight %} 85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | -------------------------------------------------------------------------------- /docs/_reference/pipe-deploy.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: pipe deploy 3 | reference: true 4 | --- 5 | 6 | ## Usage 7 | 8 | pipe deploy PIPELINE_NAME 9 | 10 | ## Description 11 | 12 | Deploy pipeline. 13 | 14 | Examples: 15 | 16 | pipe deploy 17 | pipe deploy demo # explicitly specify pipeline name 18 | pipe deploy demo -b mybranch # specify git branch 19 | 20 | The pipeline is generated from the DSL and created with CloudFormation. The files that the DSL evaluates are in the `.pipedream` folder: 21 | 22 | .pipedream/pipeline.rb 23 | .pipedream/role.rb 24 | .pipedream/schedule.rb 25 | .pipedream/webhook.rb 26 | 27 | To create the CodePipeline pipeline, you run: 28 | 29 | pipe deploy 30 | 31 | You'll see output that looks something like this: 32 | 33 | $ pipe deploy 34 | Generated CloudFormation template at /tmp/codepipeline.yml 35 | Deploying stack demo-pipe with CodePipeline project demo 36 | Creating stack demo-pipe. Check CloudFormation console for status. 37 | Stack name demo-pipe status CREATE_IN_PROGRESS 38 | Here's the CloudFormation url to check for more details https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks 39 | Waiting for stack to complete 40 | 04:14:03AM CREATE_IN_PROGRESS AWS::CloudFormation::Stack demo-pipe User Initiated 41 | 04:14:06AM CREATE_IN_PROGRESS AWS::IAM::Role IamRole 42 | 04:14:07AM CREATE_IN_PROGRESS AWS::IAM::Role IamRole Resource creation Initiated 43 | 04:14:25AM CREATE_COMPLETE AWS::IAM::Role IamRole 44 | 04:14:28AM CREATE_IN_PROGRESS AWS::CodePipeline::Pipeline Pipeline 45 | 04:14:29AM CREATE_IN_PROGRESS AWS::CodePipeline::Pipeline Pipeline Resource creation Initiated 46 | 04:14:29AM CREATE_COMPLETE AWS::CodePipeline::Pipeline Pipeline 47 | 04:14:31AM CREATE_IN_PROGRESS AWS::CodePipeline::Webhook Webhook 48 | 04:14:33AM CREATE_IN_PROGRESS AWS::CodePipeline::Webhook Webhook Resource creation Initiated 49 | 04:14:33AM CREATE_COMPLETE AWS::CodePipeline::Webhook Webhook 50 | 04:14:35AM CREATE_COMPLETE AWS::CloudFormation::Stack demo-pipe 51 | Stack success status: CREATE_COMPLETE 52 | Time took for stack deployment: 35s. 53 | $ 54 | 55 | ## Explicit Pipeline Name 56 | 57 | By default, the pipeline name is inferred and is the parent folder that you are within. You can explicitly specify the pipeline name as the first CLI argument: 58 | 59 | pipe deploy my-pipeline 60 | 61 | ## Specify Git Branch 62 | 63 | It is useful to build pipelines with different source git branches. You can pass a `--branch` option to the `pipe deploy` command. The cli `—-branch` option always takes the highest precedence. Example: 64 | 65 | pipe deploy my-pipeline --branch my-branch 66 | 67 | Note: When you specify a branch pipedream actually first updates the pipeline before starting the pipeline execution. This is done because CodePipeline does not natively support specifying the branch. It is discussed more here: [Using Different Branches]({% link _docs/examples/different-branches.md %}). 68 | 69 | 70 | ## Options 71 | 72 | ``` 73 | b, [--branch=BRANCH] # git branch 74 | [--stack-name=STACK_NAME] # Override the generated stack name. If you use this you must always specify it 75 | [--wait], [--no-wait] # Wait for operation to complete 76 | # Default: true 77 | [--verbose], [--no-verbose] 78 | [--noop], [--no-noop] 79 | ``` 80 | 81 | -------------------------------------------------------------------------------- /lib/pipedream/stack.rb: -------------------------------------------------------------------------------- 1 | require "aws-sdk-cloudformation" 2 | 3 | module Pipedream 4 | class Stack 5 | include AwsServices 6 | 7 | def initialize(options) 8 | @options = options 9 | @pipeline_name = @options[:pipeline_name] || inferred_pipeline_name 10 | @stack_name = options[:stack_name] || inferred_stack_name(@pipeline_name) 11 | 12 | @full_pipeline_name = pipeline_name_convention(@pipeline_name) 13 | @template = { 14 | "Description" => "CodePipeline Project: #{@full_pipeline_name}", 15 | "Resources" => {} 16 | } 17 | end 18 | 19 | def run 20 | options = @options.merge( 21 | pipeline_name: @pipeline_name, 22 | full_pipeline_name: @full_pipeline_name, 23 | ) 24 | 25 | pipeline_builder = Pipeline.new(options) 26 | unless pipeline_builder.exist? 27 | puts "ERROR: pipeline does not exist: #{pipeline_builder.pipeline_path}".color(:red) 28 | exit 1 29 | return 30 | end 31 | pipeline = pipeline_builder.run 32 | @template["Resources"].merge!(pipeline) 33 | 34 | if pipeline["Pipeline"]["Properties"]["RoleArn"] == {"Fn::GetAtt"=>"IamRole.Arn"} 35 | role = Role.new(options).run 36 | @template["Resources"].merge!(role) 37 | end 38 | 39 | if sns_topic?(pipeline) 40 | role = Sns.new(options).run 41 | @template["Resources"].merge!(role) 42 | end 43 | 44 | webhook = Webhook.new(options).run 45 | @template["Resources"].merge!(webhook) if webhook 46 | 47 | schedule = Schedule.new(options).run 48 | @template["Resources"].merge!(schedule) if schedule 49 | 50 | template_path = "/tmp/codepipeline.yml" 51 | FileUtils.mkdir_p(File.dirname(template_path)) 52 | IO.write(template_path, YAML.dump(@template)) 53 | puts "Generated CloudFormation template at #{template_path.color(:green)}" 54 | return if @options[:noop] 55 | puts "Deploying stack #{@stack_name.color(:green)} with CodePipeline project #{@full_pipeline_name.color(:green)}" 56 | 57 | begin 58 | perform 59 | url_info 60 | return unless @options[:wait] 61 | status.wait 62 | exit 2 unless status.success? 63 | rescue Aws::CloudFormation::Errors::ValidationError => e 64 | if e.message.include?("No updates") # No updates are to be performed. 65 | puts "WARN: #{e.message}".color(:yellow) 66 | else 67 | puts "ERROR ValidationError: #{e.message}".color(:red) 68 | exit 1 69 | end 70 | end 71 | end 72 | 73 | private 74 | def sns_topic?(template) 75 | stages = template['Pipeline']['Properties']['Stages'] 76 | stages.detect do |stage| 77 | stage['Actions'].detect do |action| 78 | action['Configuration']['NotificationArn'] == {'Ref'=>'SnsTopic'} 79 | end 80 | end 81 | end 82 | 83 | def url_info 84 | stack = cfn.describe_stacks(stack_name: @stack_name).stacks.first 85 | region = `aws configure get region`.strip rescue "us-east-1" 86 | url = "https://console.aws.amazon.com/cloudformation/home?region=#{region}#/stacks" 87 | puts "Stack name #{@stack_name.color(:yellow)} status #{stack["stack_status"].color(:yellow)}" 88 | puts "Here's the CloudFormation url to check for more details #{url}" 89 | end 90 | 91 | def status 92 | @status ||= Cfn::Status.new(@stack_name) 93 | end 94 | end 95 | end 96 | -------------------------------------------------------------------------------- /docs/_sass/_syntax.scss: -------------------------------------------------------------------------------- 1 | .highlight { background: #ffffff; } 2 | .highlight .c { color: #999988; font-style: italic } /* Comment */ 3 | .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ 4 | .highlight .k { font-weight: bold } /* Keyword */ 5 | .highlight .o { font-weight: bold } /* Operator */ 6 | .highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */ 7 | .highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */ 8 | .highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */ 9 | .highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */ 10 | .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ 11 | .highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */ 12 | .highlight .ge { font-style: italic } /* Generic.Emph */ 13 | .highlight .gr { color: #aa0000 } /* Generic.Error */ 14 | .highlight .gh { color: #999999 } /* Generic.Heading */ 15 | .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ 16 | .highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */ 17 | .highlight .go { color: #888888 } /* Generic.Output */ 18 | .highlight .gp { color: #555555 } /* Generic.Prompt */ 19 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 20 | .highlight .gu { color: #aaaaaa } /* Generic.Subheading */ 21 | .highlight .gt { color: #aa0000 } /* Generic.Traceback */ 22 | .highlight .kc { font-weight: bold } /* Keyword.Constant */ 23 | .highlight .kd { font-weight: bold } /* Keyword.Declaration */ 24 | .highlight .kp { font-weight: bold } /* Keyword.Pseudo */ 25 | .highlight .kr { font-weight: bold } /* Keyword.Reserved */ 26 | .highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */ 27 | .highlight .m { color: #009999 } /* Literal.Number */ 28 | .highlight .s { color: #d14 } /* Literal.String */ 29 | .highlight .na { color: #008080 } /* Name.Attribute */ 30 | .highlight .nb { color: #0086B3 } /* Name.Builtin */ 31 | .highlight .nc { color: #445588; font-weight: bold } /* Name.Class */ 32 | .highlight .no { color: #008080 } /* Name.Constant */ 33 | .highlight .ni { color: #800080 } /* Name.Entity */ 34 | .highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */ 35 | .highlight .nf { color: #990000; font-weight: bold } /* Name.Function */ 36 | .highlight .nn { color: #555555 } /* Name.Namespace */ 37 | .highlight .nt { color: #000080 } /* Name.Tag */ 38 | .highlight .nv { color: #008080 } /* Name.Variable */ 39 | .highlight .ow { font-weight: bold } /* Operator.Word */ 40 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 41 | .highlight .mf { color: #009999 } /* Literal.Number.Float */ 42 | .highlight .mh { color: #009999 } /* Literal.Number.Hex */ 43 | .highlight .mi { color: #009999 } /* Literal.Number.Integer */ 44 | .highlight .mo { color: #009999 } /* Literal.Number.Oct */ 45 | .highlight .sb { color: #d14 } /* Literal.String.Backtick */ 46 | .highlight .sc { color: #d14 } /* Literal.String.Char */ 47 | .highlight .sd { color: #d14 } /* Literal.String.Doc */ 48 | .highlight .s2 { color: #d14 } /* Literal.String.Double */ 49 | .highlight .se { color: #d14 } /* Literal.String.Escape */ 50 | .highlight .sh { color: #d14 } /* Literal.String.Heredoc */ 51 | .highlight .si { color: #d14 } /* Literal.String.Interpol */ 52 | .highlight .sx { color: #d14 } /* Literal.String.Other */ 53 | .highlight .sr { color: #009926 } /* Literal.String.Regex */ 54 | .highlight .s1 { color: #d14 } /* Literal.String.Single */ 55 | .highlight .ss { color: #990073 } /* Literal.String.Symbol */ 56 | .highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */ 57 | .highlight .vc { color: #008080 } /* Name.Variable.Class */ 58 | .highlight .vg { color: #008080 } /* Name.Variable.Global */ 59 | .highlight .vi { color: #008080 } /* Name.Variable.Instance */ 60 | .highlight .il { color: #009999 } /* Literal.Number.Integer.Long */ 61 | 62 | // fix css the pre code block that is generated with ``` 63 | .highlighter-rouge .highlight { 64 | background: #ecf0f1; 65 | } -------------------------------------------------------------------------------- /docs/vendor/bootstrap/css/bootstrap-reboot.min.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figcaption,figure,main{display:block}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}a:active,a:hover{outline-width:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:inherit}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}img{border-style:none}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{-webkit-box-sizing:border-box;box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details,menu{display:block}summary{display:list-item}canvas{display:inline-block}template{display:none}[hidden]{display:none}html{-webkit-box-sizing:border-box;box-sizing:border-box}*,::after,::before{-webkit-box-sizing:inherit;box-sizing:inherit}@-ms-viewport{width:device-width}html{-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}body{font-family:-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-size:1rem;font-weight:400;line-height:1.5;color:#292b2c;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{cursor:help}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}a{color:#0275d8;text-decoration:none}a:focus,a:hover{color:#014c8c;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle}[role=button]{cursor:pointer}[role=button],a,area,button,input,label,select,summary,textarea{-ms-touch-action:manipulation;touch-action:manipulation}table{border-collapse:collapse;background-color:transparent}caption{padding-top:.75rem;padding-bottom:.75rem;color:#636c72;text-align:left;caption-side:bottom}th{text-align:left}label{display:inline-block;margin-bottom:.5rem}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,select,textarea{line-height:inherit}input[type=checkbox]:disabled,input[type=radio]:disabled{cursor:not-allowed}input[type=date],input[type=time],input[type=datetime-local],input[type=month]{-webkit-appearance:listbox}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit}input[type=search]{-webkit-appearance:none}output{display:inline-block}[hidden]{display:none!important}/*# sourceMappingURL=bootstrap-reboot.min.css.map */ -------------------------------------------------------------------------------- /docs/_sass/_timeline.scss: -------------------------------------------------------------------------------- 1 | section#timeline { 2 | border-top-style: solid; 3 | border-top-width: 2px; 4 | border-top-color: #cacaca; 5 | } 6 | 7 | .timeline { 8 | position: relative; 9 | padding: 0; 10 | list-style: none; 11 | } 12 | 13 | .timeline:before { 14 | content: ""; 15 | position: absolute; 16 | top: 0; 17 | bottom: 0; 18 | left: 40px; 19 | width: 2px; 20 | margin-left: -1.5px; 21 | background-color: #f1f1f1; 22 | } 23 | 24 | .timeline>li { 25 | position: relative; 26 | margin-bottom: 50px; 27 | min-height: 50px; 28 | } 29 | 30 | .timeline>li:before, 31 | .timeline>li:after { 32 | content: " "; 33 | display: table; 34 | } 35 | 36 | .timeline>li:after { 37 | clear: both; 38 | } 39 | 40 | .timeline>li .timeline-panel { 41 | float: right; 42 | position: relative; 43 | width: 100%; 44 | padding: 0 20px 0 100px; 45 | text-align: left; 46 | } 47 | 48 | .timeline>li .timeline-panel:before { 49 | right: auto; 50 | left: -15px; 51 | border-right-width: 15px; 52 | border-left-width: 0; 53 | } 54 | 55 | .timeline>li .timeline-panel:after { 56 | right: auto; 57 | left: -14px; 58 | border-right-width: 14px; 59 | border-left-width: 0; 60 | } 61 | 62 | .timeline>li .timeline-image { 63 | z-index: 100; 64 | position: absolute; 65 | left: 0; 66 | width: 80px; 67 | height: 80px; 68 | margin-left: 0; 69 | border: 7px solid #f1f1f1; 70 | border-radius: 100%; 71 | text-align: center; 72 | color: #fff; 73 | background-color: #fed136; 74 | } 75 | 76 | .timeline>li .timeline-image h4 { 77 | margin-top: 26px; 78 | font-size: 18px; 79 | line-height: 14px; 80 | } 81 | 82 | .timeline>li.timeline-inverted>.timeline-panel { 83 | float: right; 84 | padding: 0 20px 0 100px; 85 | text-align: left; 86 | } 87 | 88 | .timeline>li.timeline-inverted>.timeline-panel:before { 89 | right: auto; 90 | left: -15px; 91 | border-right-width: 15px; 92 | border-left-width: 0; 93 | } 94 | 95 | .timeline>li.timeline-inverted>.timeline-panel:after { 96 | right: auto; 97 | left: -14px; 98 | border-right-width: 14px; 99 | border-left-width: 0; 100 | } 101 | 102 | .timeline>li:last-child { 103 | margin-bottom: 0; 104 | } 105 | 106 | .timeline .timeline-heading h4 { 107 | margin-top: 0; 108 | color: inherit; 109 | } 110 | 111 | .timeline .timeline-heading h4.subheading { 112 | text-transform: none; 113 | } 114 | 115 | .timeline .timeline-body>p, 116 | .timeline .timeline-body>ul { 117 | margin-bottom: 0; 118 | } 119 | 120 | @media(min-width:768px) { 121 | .timeline:before { 122 | left: 50%; 123 | } 124 | 125 | .timeline>li { 126 | margin-bottom: 100px; 127 | min-height: 100px; 128 | } 129 | 130 | .timeline>li .timeline-panel { 131 | float: left; 132 | width: 41%; 133 | padding: 0 20px 20px 30px; 134 | text-align: right; 135 | } 136 | 137 | .timeline>li .timeline-image { 138 | left: 50%; 139 | width: 100px; 140 | height: 100px; 141 | margin-left: -50px; 142 | } 143 | 144 | .timeline>li .timeline-image h4 { 145 | margin-top: 32px; 146 | font-size: 20px; 147 | line-height: 18px; 148 | } 149 | 150 | .timeline>li.timeline-inverted>.timeline-panel { 151 | float: right; 152 | padding: 0 30px 20px 20px; 153 | text-align: left; 154 | } 155 | } 156 | 157 | @media(min-width:992px) { 158 | .timeline>li { 159 | min-height: 150px; 160 | } 161 | 162 | .timeline>li .timeline-panel { 163 | padding: 0 20px 20px; 164 | } 165 | 166 | .timeline>li .timeline-image { 167 | width: 150px; 168 | height: 150px; 169 | margin-left: -75px; 170 | } 171 | 172 | .timeline>li .timeline-image h4 { 173 | margin-top: 55px; 174 | font-size: 38px; 175 | line-height: 26px; 176 | } 177 | 178 | .timeline>li.timeline-inverted>.timeline-panel { 179 | padding: 0 20px 20px; 180 | } 181 | } 182 | 183 | @media(min-width:1200px) { 184 | .timeline>li { 185 | min-height: 170px; 186 | } 187 | 188 | .timeline>li .timeline-panel { 189 | padding: 0 20px 20px 100px; 190 | } 191 | 192 | .timeline>li .timeline-image { 193 | width: 170px; 194 | height: 170px; 195 | margin-left: -85px; 196 | } 197 | 198 | .timeline>li .timeline-image h4 { 199 | /*margin-top: 40px;*/ 200 | margin-top: 65px; 201 | font-size: 42px; 202 | } 203 | 204 | .timeline>li.timeline-inverted>.timeline-panel { 205 | padding: 0 100px 20px 20px; 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /docs/vendor/jquery-easing/jquery.easing.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery Easing v1.4.1 - http://gsgd.co.uk/sandbox/jquery/easing/ 3 | * Open source under the BSD License. 4 | * Copyright © 2008 George McGinley Smith 5 | * All rights reserved. 6 | * https://raw.github.com/gdsmith/jquery-easing/master/LICENSE 7 | */ 8 | 9 | (function (factory) { 10 | if (typeof define === "function" && define.amd) { 11 | define(['jquery'], function ($) { 12 | return factory($); 13 | }); 14 | } else if (typeof module === "object" && typeof module.exports === "object") { 15 | exports = factory(require('jquery')); 16 | } else { 17 | factory(jQuery); 18 | } 19 | })(function($){ 20 | 21 | // Preserve the original jQuery "swing" easing as "jswing" 22 | $.easing.jswing = $.easing.swing; 23 | 24 | var pow = Math.pow, 25 | sqrt = Math.sqrt, 26 | sin = Math.sin, 27 | cos = Math.cos, 28 | PI = Math.PI, 29 | c1 = 1.70158, 30 | c2 = c1 * 1.525, 31 | c3 = c1 + 1, 32 | c4 = ( 2 * PI ) / 3, 33 | c5 = ( 2 * PI ) / 4.5; 34 | 35 | // x is the fraction of animation progress, in the range 0..1 36 | function bounceOut(x) { 37 | var n1 = 7.5625, 38 | d1 = 2.75; 39 | if ( x < 1/d1 ) { 40 | return n1*x*x; 41 | } else if ( x < 2/d1 ) { 42 | return n1*(x-=(1.5/d1))*x + 0.75; 43 | } else if ( x < 2.5/d1 ) { 44 | return n1*(x-=(2.25/d1))*x + 0.9375; 45 | } else { 46 | return n1*(x-=(2.625/d1))*x + 0.984375; 47 | } 48 | } 49 | 50 | $.extend( $.easing, 51 | { 52 | def: 'easeOutQuad', 53 | swing: function (x) { 54 | return $.easing[$.easing.def](x); 55 | }, 56 | easeInQuad: function (x) { 57 | return x * x; 58 | }, 59 | easeOutQuad: function (x) { 60 | return 1 - ( 1 - x ) * ( 1 - x ); 61 | }, 62 | easeInOutQuad: function (x) { 63 | return x < 0.5 ? 64 | 2 * x * x : 65 | 1 - pow( -2 * x + 2, 2 ) / 2; 66 | }, 67 | easeInCubic: function (x) { 68 | return x * x * x; 69 | }, 70 | easeOutCubic: function (x) { 71 | return 1 - pow( 1 - x, 3 ); 72 | }, 73 | easeInOutCubic: function (x) { 74 | return x < 0.5 ? 75 | 4 * x * x * x : 76 | 1 - pow( -2 * x + 2, 3 ) / 2; 77 | }, 78 | easeInQuart: function (x) { 79 | return x * x * x * x; 80 | }, 81 | easeOutQuart: function (x) { 82 | return 1 - pow( 1 - x, 4 ); 83 | }, 84 | easeInOutQuart: function (x) { 85 | return x < 0.5 ? 86 | 8 * x * x * x * x : 87 | 1 - pow( -2 * x + 2, 4 ) / 2; 88 | }, 89 | easeInQuint: function (x) { 90 | return x * x * x * x * x; 91 | }, 92 | easeOutQuint: function (x) { 93 | return 1 - pow( 1 - x, 5 ); 94 | }, 95 | easeInOutQuint: function (x) { 96 | return x < 0.5 ? 97 | 16 * x * x * x * x * x : 98 | 1 - pow( -2 * x + 2, 5 ) / 2; 99 | }, 100 | easeInSine: function (x) { 101 | return 1 - cos( x * PI/2 ); 102 | }, 103 | easeOutSine: function (x) { 104 | return sin( x * PI/2 ); 105 | }, 106 | easeInOutSine: function (x) { 107 | return -( cos( PI * x ) - 1 ) / 2; 108 | }, 109 | easeInExpo: function (x) { 110 | return x === 0 ? 0 : pow( 2, 10 * x - 10 ); 111 | }, 112 | easeOutExpo: function (x) { 113 | return x === 1 ? 1 : 1 - pow( 2, -10 * x ); 114 | }, 115 | easeInOutExpo: function (x) { 116 | return x === 0 ? 0 : x === 1 ? 1 : x < 0.5 ? 117 | pow( 2, 20 * x - 10 ) / 2 : 118 | ( 2 - pow( 2, -20 * x + 10 ) ) / 2; 119 | }, 120 | easeInCirc: function (x) { 121 | return 1 - sqrt( 1 - pow( x, 2 ) ); 122 | }, 123 | easeOutCirc: function (x) { 124 | return sqrt( 1 - pow( x - 1, 2 ) ); 125 | }, 126 | easeInOutCirc: function (x) { 127 | return x < 0.5 ? 128 | ( 1 - sqrt( 1 - pow( 2 * x, 2 ) ) ) / 2 : 129 | ( sqrt( 1 - pow( -2 * x + 2, 2 ) ) + 1 ) / 2; 130 | }, 131 | easeInElastic: function (x) { 132 | return x === 0 ? 0 : x === 1 ? 1 : 133 | -pow( 2, 10 * x - 10 ) * sin( ( x * 10 - 10.75 ) * c4 ); 134 | }, 135 | easeOutElastic: function (x) { 136 | return x === 0 ? 0 : x === 1 ? 1 : 137 | pow( 2, -10 * x ) * sin( ( x * 10 - 0.75 ) * c4 ) + 1; 138 | }, 139 | easeInOutElastic: function (x) { 140 | return x === 0 ? 0 : x === 1 ? 1 : x < 0.5 ? 141 | -( pow( 2, 20 * x - 10 ) * sin( ( 20 * x - 11.125 ) * c5 )) / 2 : 142 | pow( 2, -20 * x + 10 ) * sin( ( 20 * x - 11.125 ) * c5 ) / 2 + 1; 143 | }, 144 | easeInBack: function (x) { 145 | return c3 * x * x * x - c1 * x * x; 146 | }, 147 | easeOutBack: function (x) { 148 | return 1 + c3 * pow( x - 1, 3 ) + c1 * pow( x - 1, 2 ); 149 | }, 150 | easeInOutBack: function (x) { 151 | return x < 0.5 ? 152 | ( pow( 2 * x, 2 ) * ( ( c2 + 1 ) * 2 * x - c2 ) ) / 2 : 153 | ( pow( 2 * x - 2, 2 ) *( ( c2 + 1 ) * ( x * 2 - 2 ) + c2 ) + 2 ) / 2; 154 | }, 155 | easeInBounce: function (x) { 156 | return 1 - bounceOut( 1 - x ); 157 | }, 158 | easeOutBounce: bounceOut, 159 | easeInOutBounce: function (x) { 160 | return x < 0.5 ? 161 | ( 1 - bounceOut( 1 - 2 * x ) ) / 2 : 162 | ( 1 + bounceOut( 2 * x - 1 ) ) / 2; 163 | } 164 | }); 165 | 166 | }); 167 | --------------------------------------------------------------------------------