├── .eslintignore ├── .prettierignore ├── .gitattributes ├── .prettierrc.json ├── docs ├── images │ ├── artifact.png │ ├── branch-protection.png │ ├── works-on-my-machine.png │ ├── branch-protection-pr-merge.png │ ├── branch-protection-ui-commit.png │ ├── branch-protection-cli-commit.png │ └── gitflow.png ├── SUPPORT.md ├── ssh-keys.md ├── development.md ├── SECURITY.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── advanced-usage.md ├── CHANGELOG.md ├── basic-usage.md └── inputs.md ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── Bug.md │ └── Feature.md ├── workflows │ ├── build.yml │ ├── test.workflow.push.yml │ ├── copybara-docker.yml │ └── test.workflow.pr.yml ├── Dockerfile └── pull_request_template.md ├── jest.config.js ├── tsconfig.json ├── .config ├── autodoc.js ├── commit-docs.sh ├── commit-build.sh ├── commitlint.config.js ├── commit-convention.json ├── cz-config.js └── release.config.js ├── .gitignore ├── .eslintrc.json ├── README.md ├── src ├── github.ts ├── main.ts ├── hostConfig.ts ├── copy.bara.sky.ts ├── exit.ts ├── copybara.ts └── copybaraAction.ts ├── package.json ├── action.yml ├── LICENSE └── dist └── licenses.txt /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | lib/ 3 | node_modules/ -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | lib/ 3 | node_modules/ -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | dist/** -diff linguist-generated=true -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120 3 | } 4 | -------------------------------------------------------------------------------- /docs/images/artifact.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailchain/copybara-action/main/docs/images/artifact.png -------------------------------------------------------------------------------- /docs/images/branch-protection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailchain/copybara-action/main/docs/images/branch-protection.png -------------------------------------------------------------------------------- /docs/images/works-on-my-machine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailchain/copybara-action/main/docs/images/works-on-my-machine.png -------------------------------------------------------------------------------- /docs/images/branch-protection-pr-merge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailchain/copybara-action/main/docs/images/branch-protection-pr-merge.png -------------------------------------------------------------------------------- /docs/images/branch-protection-ui-commit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailchain/copybara-action/main/docs/images/branch-protection-ui-commit.png -------------------------------------------------------------------------------- /docs/images/branch-protection-cli-commit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailchain/copybara-action/main/docs/images/branch-protection-cli-commit.png -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Support and Questions 4 | about: Do not open issues for questions 5 | url: 'https://keybase.io/team/olivr' 6 | -------------------------------------------------------------------------------- /docs/SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Get Support 2 | 3 | - For questions about this action: [Join Olivr on Keybase](https://keybase.io/team/olivr) 4 | - For questions about Copybara: [Copybara's repo](https://github.com/google/copybara/) 5 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | clearMocks: true, 3 | moduleFileExtensions: ['js', 'ts'], 4 | testEnvironment: 'node', 5 | testMatch: ['**/*.test.ts'], 6 | testRunner: 'jest-circus/runner', 7 | transform: { 8 | '^.+\\.ts$': 'ts-jest' 9 | }, 10 | verbose: true 11 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "strict": true, 6 | "noImplicitAny": true, 7 | "moduleResolution": "node", 8 | "esModuleInterop": true 9 | }, 10 | "exclude": ["node_modules", "**/*.test.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /.config/autodoc.js: -------------------------------------------------------------------------------- 1 | const YAML = require("yaml"); 2 | const tablemark = require("tablemark"); 3 | const fs = require("fs"); 4 | 5 | const file = fs.readFileSync("./action.yml", "utf8"); 6 | 7 | const yml = YAML.parse(file); 8 | const table = []; 9 | 10 | Object.keys(yml.inputs).forEach((input) => { 11 | table.push({ 12 | input, 13 | required: yml.inputs[input] && yml.inputs[input].required ? "yes" : "", 14 | default: yml.inputs[input] && yml.inputs[input].default ? yml.inputs[input].default : "", 15 | description: yml.inputs[input] && yml.inputs[input].description ? yml.inputs[input].description : "", 16 | }); 17 | }); 18 | 19 | fs.writeFileSync("./docs/inputs.md", tablemark(table)); 20 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Test build 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | build: 9 | name: Test build 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | 14 | - name: Setup Node.js 15 | uses: actions/setup-node@v2 16 | with: 17 | node-version: 14 18 | 19 | - name: Install dependencies 20 | run: yarn install --frozen-lockfile 21 | 22 | - name: Build 23 | run: yarn build 24 | 25 | - name: Ensure build was committed (necessary for Github Actions) 26 | run: "[[ -z $(git status -s dist) ]]" 27 | 28 | - name: "[Test] Unit tests" 29 | run: yarn test 30 | -------------------------------------------------------------------------------- /.config/commit-docs.sh: -------------------------------------------------------------------------------- 1 | # This script is used for repos that need to commit a build folder (eg. Github actions) 2 | 3 | # Add more sources/destinations by separating them by spaces (ie. source="src lib") 4 | source="action.yml" 5 | build="docs/inputs.md" 6 | 7 | # If some changes in $source are staged 8 | if [[ $(git status --porcelain $source | egrep '^M') ]] 9 | 10 | then 11 | 12 | # Run build command 13 | echo "Generating $build..." 14 | node .config/autodoc.js 15 | 16 | # If this generated unstaged changes in $build folder(s) 17 | if [[ $(git status --porcelain $build | egrep '^(([M ]M)|\?\?)') ]] 18 | 19 | then 20 | 21 | git add $build 22 | echo "ℹ️ Added $build to your commit" 23 | 24 | fi 25 | 26 | fi -------------------------------------------------------------------------------- /.config/commit-build.sh: -------------------------------------------------------------------------------- 1 | # This script is used for repos that need to commit a build folder (eg. Github actions) 2 | 3 | # Add more sources/destinations by separating them by spaces (ie. source="src lib") 4 | source="src" 5 | build="dist" 6 | 7 | # If some changes in $source are staged 8 | if [[ $(git status --porcelain $source | egrep '^M') ]] 9 | 10 | then 11 | 12 | # Run build command 13 | echo "Verifying $build is built and committed..." 14 | yarn build -q 15 | 16 | # If this generated unstaged changes in $build folder(s) 17 | if [[ $(git status --porcelain $build | egrep '^(([M ]M)|\?\?)') ]] 18 | 19 | then 20 | 21 | yarn test 22 | git add $build 23 | echo "ℹ️ Added $build to your commit" 24 | 25 | fi 26 | 27 | fi -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependency directory 2 | node_modules 3 | 4 | # Rest pulled from https://github.com/github/gitignore/blob/master/Node.gitignore 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | lerna-debug.log* 12 | 13 | # Directory for instrumented libs generated by jscoverage/JSCover 14 | lib-cov 15 | 16 | # Coverage directory used by tools like istanbul 17 | coverage 18 | *.lcov 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # TypeScript v1 declaration files 24 | typings/ 25 | 26 | # TypeScript cache 27 | *.tsbuildinfo 28 | 29 | # Optional npm cache directory 30 | .npm 31 | 32 | # Optional eslint cache 33 | .eslintcache 34 | 35 | # Optional REPL history 36 | .node_repl_history 37 | 38 | # Output of 'npm pack' 39 | *.tgz 40 | 41 | # Yarn Integrity file 42 | .yarn-integrity 43 | 44 | # dotenv environment variables file 45 | .env 46 | .env.test 47 | .secrets 48 | 49 | # OS metadata 50 | .DS_Store 51 | Thumbs.db 52 | 53 | # Ignore built ts files 54 | __tests__/runner/* -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es2020": true, 4 | "node": true, 5 | "jest": true 6 | }, 7 | "ignorePatterns": ["dist/*"], 8 | "plugins": ["node", "jest", "@typescript-eslint"], 9 | "extends": [ 10 | "eslint:recommended", 11 | "plugin:import/errors", 12 | "plugin:import/warnings", 13 | "plugin:import/typescript", 14 | "plugin:node/recommended", 15 | "plugin:jest/recommended", 16 | "plugin:jest/style", 17 | "plugin:@typescript-eslint/recommended", 18 | "plugin:prettier/recommended", 19 | "prettier", 20 | "prettier/@typescript-eslint" 21 | ], 22 | "parser": "@typescript-eslint/parser", 23 | "parserOptions": { 24 | "ecmaVersion": 9, 25 | "sourceType": "module", 26 | "project": "./tsconfig.json" 27 | }, 28 | "settings": { 29 | "node": { 30 | "tryExtensions": [".js", ".json", ".node", ".ts"] 31 | } 32 | }, 33 | "rules": { 34 | "node/no-unsupported-features/es-syntax": 0, 35 | "node/no-unsupported-features/es-builtins": 0, 36 | "node/no-extraneous-import": [ 37 | "error", 38 | { 39 | "allowModules": ["@jest/globals"] 40 | } 41 | ] 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /docs/ssh-keys.md: -------------------------------------------------------------------------------- 1 | # SSH Keys 2 | 3 | You can use a [personal key](https://docs.github.com/en/github/authenticating-to-github/connecting-to-github-with-ssh) or [deploy keys](https://docs.github.com/en/developers/overview/managing-deploy-keys). 4 | 5 | If using a personal key, you can use the same key for both repositories. 6 | 7 | If using deploy keys, **you will need two**: 8 | 9 | - One with write access to SoT 10 | - One with write access to Destination 11 | 12 | > The recommended way is to use deploy keys 13 | 14 | ## Usage 15 | 16 | Depending on your choice, you will need to attach the **public key** to a user (personal key) or a repository (deploy key). 17 | 18 | In both your SoT and destination repos, add a new secret `SSH_KEY` and copy the **private key** as the value. 19 | 20 | ## Generate a private/public key pair 21 | 22 | ![it works on my machine](images/works-on-my-machine.png) 23 | 24 | ```sh 25 | ssh-keygen -N "" -m PEM -t rsa -f temp_key -q 26 | 27 | echo "\n\nPrivate key to copy in the SSH_KEY secret of first repo:\n\n" 28 | cat temp_key 29 | 30 | echo "\n\n\n\nPublic key to copy in your profile or as a deploy key of second repo (with write access):\n\n" 31 | cat temp_key.pub 32 | 33 | rm -rf temp_key temp_key.pub 34 | ``` 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Report an issue you found with Copybara Action 4 | labels: bug 5 | --- 6 | 7 | 13 | 14 | ## :beetle: Bug report 15 | 16 | ### Summary 17 | 18 | 19 | 20 | ### Expected behavior 21 | 22 | 23 | 24 | ### Actual behavior 25 | 26 | 27 | 28 | ### Steps to reproduce the problem 29 | 30 | 34 | 35 | 36 | 37 | ### Environment 38 | 39 | 43 | 44 | - Version: 45 | 46 | ### Possible fix 47 | 48 | 52 | -------------------------------------------------------------------------------- /.config/commitlint.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This configuration is based on the content of cz-config.js 3 | * Check it out first before modifying this file 4 | */ 5 | 6 | const { types, scopes, scopeOverrides, allowCustomScopes, upperCaseSubject } = require(__dirname + "/cz-config.js"); 7 | 8 | /** 9 | * Types 10 | */ 11 | 12 | // Determine if types should be sentence-case based on wether the first type is 13 | const upperCaseType = types[0].value.charAt(0).toUpperCase() === types[0].value.charAt(0); 14 | 15 | // Determine valid types 16 | const validTypes = types.map((type) => type.value); 17 | 18 | /** 19 | * Scopes 20 | */ 21 | 22 | // Valid scopes are any of scopes and scopes overrides 23 | let validScopes = scopes.map((scope) => scope.name); 24 | Object.keys(scopeOverrides).forEach((k) => Array.prototype.push.apply(validScopes, scopeOverrides[k])); 25 | 26 | // If custom scopes are not allowed, throw an error 27 | // Otherwise only warn if the commit contains a custom scope 28 | const scopeValidationLevel = allowCustomScopes ? 1 : 2; 29 | 30 | module.exports = { 31 | extends: ["@commitlint/config-conventional"], 32 | 33 | // Add your own rules. See https://commitlint.js.org/#/reference-rules 34 | rules: { 35 | "scope-enum": [scopeValidationLevel, "always", validScopes], 36 | "type-enum": [2, "always", validTypes], 37 | "subject-case": upperCaseSubject ? [1, "always", ["sentence-case"]] : undefined, 38 | "type-case": upperCaseType ? [1, "always", ["sentence-case"]] : undefined, 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /docs/development.md: -------------------------------------------------------------------------------- 1 | # Development 2 | 3 | Install the dependencies 4 | 5 | ```sh 6 | yarn install 7 | ``` 8 | 9 | Build the typescript and package it for distribution 10 | 11 | ```sh 12 | yarn build 13 | ``` 14 | 15 | Run the tests :heavy_check_mark: 16 | 17 | ```sh 18 | yarn test 19 | ``` 20 | 21 | > As of writing this doc, there are no unit tests, just some E2E tests in `.github/workflows/test.*` which are run on every merge to `main`. **If you want to add unit tests or more E2E tests, please do so!** 22 | 23 | ## Debugging 24 | 25 | In your repos, set the secret `ACTIONS_STEP_DEBUG` to `true` 26 | 27 | The action outputs will be more verbose and you will also be able to download the generated `copy.bara.sky` configuration file. 28 | 29 | ![artifact](images/artifact.png) 30 | 31 | ## Validation 32 | 33 | You can use [act](https://github.com/nektos/act) for faster feedback at least until Docker is run (Copybara download/run). I haven't had time to look into configuring act to load a runner with docker support. 34 | 35 | 1. Create file `.secrets` 36 | 37 | ```text 38 | GH_TOKEN="xxxxxxxxxxxxxxxxxxxxx" 39 | SSH_KEY="-----BEGIN OPENSSH PRIVATE KEY-----xxxxxxxxxxxxxxx-----END OPENSSH PRIVATE KEY-----" 40 | ``` 41 | 42 | 2. Run `act --secret-file=.secrets` 43 | 44 | ## Maintainers: Publish to a distribution branch 45 | 46 | ```sh 47 | yarn run package 48 | git add dist 49 | git commit -m "Release" 50 | git push origin releases/v1 51 | ``` 52 | 53 | See the [versioning documentation](https://github.com/actions/toolkit/blob/master/docs/action-versioning.md) 54 | -------------------------------------------------------------------------------- /.config/commit-convention.json: -------------------------------------------------------------------------------- 1 | { 2 | "enable": { 3 | "scopes": false, 4 | "customScopes": true 5 | }, 6 | "sentenceCase": { 7 | "type": true, 8 | "scope": false, 9 | "subject": true 10 | }, 11 | "types": [ 12 | { 13 | "name": "feat", 14 | "description": "feat: A new feature", 15 | "logSection": "Features", 16 | "releaseType": "minor", 17 | "allowBreakingChange": true 18 | }, 19 | { 20 | "name": "fix", 21 | "description": "fix: A bug fix", 22 | "logSection": "Bug Fixes", 23 | "releaseType": "patch", 24 | "allowBreakingChange": true 25 | }, 26 | { 27 | "name": "perf", 28 | "description": "perf: A code change that improves performance", 29 | "releaseType": "patch", 30 | "allowBreakingChange": true 31 | }, 32 | { 33 | "name": "docs", 34 | "description": "docs: Documentation only changes" 35 | }, 36 | { 37 | "name": "chore", 38 | "description": "chore: Changes to the build process or auxiliary tools", 39 | "scopeOverrides": ["build", "ci"] 40 | }, 41 | { 42 | "name": "style", 43 | "description": "style: Changes that do not affect the meaning of the code\n (white-space, formatting, missing semi-colons, etc)" 44 | }, 45 | { 46 | "name": "refactor", 47 | "description": "refactor: A code change that neither fixes a bug nor adds a feature" 48 | }, 49 | { 50 | "name": "test", 51 | "description": "test: Adding missing tests" 52 | } 53 | ], 54 | "scopes": ["parser", "lang"] 55 | } 56 | -------------------------------------------------------------------------------- /.github/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | FROM gcr.io/bazel-public/bazel:6.0.0 AS build 16 | 17 | USER root 18 | COPY . . 19 | RUN ./cloudbuild.sh build //java/com/google/copybara:copybara_deploy.jar 20 | RUN mkdir -p /tmp/copybara && \ 21 | cp bazel-bin/java/com/google/copybara/copybara_deploy.jar /tmp/copybara/ 22 | 23 | USER ubuntu 24 | FROM golang:latest AS buildtools 25 | RUN go install github.com/bazelbuild/buildtools/buildozer@latest 26 | RUN go install github.com/bazelbuild/buildtools/buildifier@latest 27 | 28 | FROM openjdk:11-jre-slim 29 | WORKDIR /usr/src/app 30 | ENV COPYBARA_CONFIG=copy.bara.sky \ 31 | COPYBARA_SUBCOMMAND=migrate \ 32 | COPYBARA_OPTIONS='' \ 33 | COPYBARA_WORKFLOW=default \ 34 | COPYBARA_SOURCEREF='' 35 | COPY --from=build /tmp/copybara/ /opt/copybara/ 36 | COPY --from=buildtools /go/bin/buildozer /go/bin/buildifier /usr/bin/ 37 | COPY .docker/entrypoint.sh /usr/local/bin/copybara 38 | RUN chmod +x /usr/local/bin/copybara 39 | RUN apt-get update && \ 40 | apt-get install -y git && \ 41 | apt-get clean -------------------------------------------------------------------------------- /.config/cz-config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Configuration file for commitizen customizable preset 3 | * https://github.com/leonardoanalista/cz-customizable#options 4 | * https://github.com/leonardoanalista/cz-customizable/blob/master/cz-config-EXAMPLE.js 5 | */ 6 | 7 | // Import your custom convention configuration 8 | const convention = require(__dirname + '/commit-convention.json') 9 | 10 | // Enforce convention's sentence case settings 11 | const sentenceCase = (str, sentenceCase = true) => 12 | str.replace(/^./, sentenceCase ? str[0].toUpperCase() : str[0].toLowerCase()) 13 | 14 | // Types 15 | const types = convention.types.map(({ name, description }) => ({ 16 | value: sentenceCase(name, convention.sentenceCase.type), 17 | name: sentenceCase(description, convention.sentenceCase.type), 18 | })) 19 | 20 | // Scopes 21 | const scopes = convention.scopes.map((name) => ({ 22 | name: sentenceCase(name, convention.sentenceCase.scope), 23 | })) 24 | 25 | const scopeOverrides = {} 26 | const allowBreakingChanges = [] 27 | 28 | convention.types.forEach(({ name, scopeOverrides, allowBreakingChange }) => { 29 | // Types that can contain breaking changes 30 | if (allowBreakingChange === true) allowBreakingChanges.push(name) 31 | 32 | // Types with scopes overrides 33 | if (Array.isArray(scopeOverrides)) 34 | scopeOverrides[name] = scopeOverrides.map((scope) => 35 | sentenceCase(scope, convention.sentenceCase.scope) 36 | ) 37 | }) 38 | 39 | module.exports = { 40 | types, 41 | scopes, 42 | scopeOverrides, 43 | allowBreakingChanges, 44 | allowCustomScopes: convention.enable.customScopes, 45 | skipQuestions: [convention.enable.scopes ? '' : 'scope'], 46 | upperCaseSubject: convention.sentenceCase.subject, 47 | } 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Copybara Action 2 | 3 | Google's [Copybara](https://github.com/google/copybara) is a great tool for transforming and moving code between repositories. 4 | 5 | This action comes with sensible defaults to make it **very easy** for you to use Copybara with Github but is also **100% customizable** so you can use it with your own config, your own Docker image. 6 | 7 | ## ♾️ Default flow 8 | 9 | ```text 10 | Source of Truth Destination 11 | 12 | +---------------+ Copybara +---------------+ 13 | | Branch +------------> | Branch | 14 | +-------+-------+ +---------------+ 15 | ^ 16 | | 17 | | 18 | +-------+-------+ Copybara +---------------+ 19 | | Pull Requests | <------------+ Pull Requests | 20 | +---------------+ +---------------+ 21 | ``` 22 | 23 | - One repo acts as the Source of Truth (SoT) 24 | - One other repo acts as the destination 25 | - SoT branch is always pushed by Copybara to destination branch 26 | - Pull Requests can be created on both SoT and destination 27 | - Pull Requests created on destination are always copied by Copybara to SoT 28 | 29 | > This is the flow used for this action's [basic usage](docs/basic-usage.md), you can make it whatever you want it to be in [advanced usage](docs/advanced-usage.md). 30 | 31 | ## 🔥 [Basic usage](docs/basic-usage.md) 32 | 33 | ## 🧨 [Advanced usage](docs/advanced-usage.md) 34 | 35 | ## 🔘 [All options](docs/inputs.md) 36 | 37 | ## 💚 [Contributing](docs/CONTRIBUTING.md) 38 | 39 | ## 💬 Support 40 | 41 | - For questions about this action: [Join Oliv'r on Keybase](https://keybase.io/team/olivr) 42 | - For questions about Copybara: [Copybara's repo](https://github.com/google/copybara/) 43 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Proposal 3 | about: Tell us how Copybara Action can serve you better 4 | labels: enhancement 5 | --- 6 | 7 | 13 | 14 | ## :bulb: Feature proposal 15 | 16 | ### Use-cases 17 | 18 | 24 | 25 | ### Example 26 | 27 | 28 | 29 | ### Attempted solutions 30 | 31 | 35 | 36 | ### Proposal 37 | 38 | 43 | 44 | 45 | 46 | ### References 47 | 48 | 57 | -------------------------------------------------------------------------------- /.config/release.config.js: -------------------------------------------------------------------------------- 1 | // Import your custom convention configuration 2 | const convention = require(__dirname + "/commit-convention.json"); 3 | 4 | // Enforce convention's sentence case settings 5 | const sentenceCase = (str, sentenceCase = true) => 6 | str.replace(/^./, sentenceCase ? str[0].toUpperCase() : str[0].toLowerCase()); 7 | 8 | // Release rules for each type 9 | const releaseRules = convention.types.map(({ name, releaseType }) => ({ 10 | type: sentenceCase(name, convention.sentenceCase.type), 11 | release: releaseType || false, 12 | })); 13 | 14 | // Changelog sections for each type 15 | const logSections = convention.types.map(({ name, logSection }) => ({ 16 | type: sentenceCase(name, false), 17 | section: logSection, 18 | hidden: logSection ? false : true, 19 | })); 20 | 21 | module.exports = { 22 | branches: ["main"], 23 | plugins: [ 24 | [ 25 | "@semantic-release/commit-analyzer", 26 | { 27 | preset: "conventionalcommits", 28 | releaseRules, 29 | }, 30 | ], 31 | ["@semantic-release/changelog", { changelogFile: "docs/CHANGELOG.md" }], 32 | [ 33 | "@semantic-release/release-notes-generator", 34 | { 35 | preset: "conventionalcommits", 36 | presetConfig: { types: logSections }, 37 | }, 38 | ], 39 | [ 40 | "@semantic-release/exec", 41 | { 42 | prepareCmd: 43 | "find docs/*.md -type f -exec sed -i -E 's/copybara-action@[^s]+/copybara-action@v${nextRelease.version}/g' {} +", 44 | }, 45 | ], 46 | [ 47 | "@semantic-release/git", 48 | { 49 | assets: ["docs", "package.json"], 50 | message: "Chore(release): ${nextRelease.version}\n\n${nextRelease.notes}", 51 | }, 52 | ], 53 | ["@semantic-release/github"], 54 | ], 55 | }; 56 | -------------------------------------------------------------------------------- /src/github.ts: -------------------------------------------------------------------------------- 1 | import { getOctokit } from "@actions/github"; 2 | import { GitHub as ghAPI } from "@actions/github/lib/utils"; 3 | 4 | export class GitHub { 5 | api: InstanceType; 6 | 7 | constructor(accessToken: string) { 8 | this.api = getOctokit(accessToken); 9 | } 10 | 11 | async branchExists(repoFullName: string, branch: string, createRepo: boolean): Promise { 12 | const [owner, repo] = repoFullName.split("/"); 13 | 14 | return this.repoExists(owner, repo, createRepo).then((repoExists) => { 15 | if (repoExists) { 16 | return this.api.repos 17 | .getBranch({ owner, repo, branch }) 18 | .then((res) => res.status == 200) 19 | .catch((err) => { 20 | if (err.status == 404) return false; 21 | else throw err; 22 | }); 23 | } else return false; 24 | }); 25 | } 26 | 27 | async getDefaultBranch(repoFullName: string): Promise { 28 | const [owner, repo] = repoFullName.split("/"); 29 | return this.api.repos.get({ owner, repo }).then((res) => res.data.default_branch); 30 | } 31 | 32 | private async repoExists(owner: string, repo: string, createRepo: boolean): Promise { 33 | return this.api.repos 34 | .get({ owner, repo }) 35 | .then((res) => res.status == 200) 36 | .catch((err) => { 37 | if (err.status == 404) { 38 | if (createRepo) return this.createRepo(owner, repo).then((res) => res.status == 200); 39 | else return false; 40 | } else throw err; 41 | }); 42 | } 43 | 44 | private async createRepo(owner: string, repo: string) { 45 | const currentUser = await this.api.users.getAuthenticated().then((res) => res.data.name); 46 | 47 | return currentUser == owner 48 | ? this.api.repos.createForAuthenticatedUser({ name: repo }) 49 | : this.api.repos.createInOrg({ org: owner, name: repo }); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /docs/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | Although we do our best to keep Copybara Action secure. Vulnerabilities can happen. If you think you found a vulnerability, we appreciate your efforts to responsibly disclose your findings. 4 | 5 | ## Reporting a Vulnerability 6 | 7 | Report security bugs by contacting us on romain-noreply@barissat.com. Report all other bugs on our GitHub issues page. 8 | 9 | If you are not sure, don’t worry. Better safe than sorry – just get in touch. Do not open issues related to any security concerns publicly. 10 | 11 | When reporting an issue, include as much information as possible, but no need to fill fancy forms or answer tedious questions. Just tell us what you found, how to reproduce it, and any concerns you have about it. We will respond as soon as possible and follow up with any missing information. 12 | 13 | We will acknowledge your email and will send a more detailed response shortly after that indicating the next steps in handling your report. After the initial reply to your report, we will endeavor to keep you informed of the progress towards a fix, and may ask for additional information or guidance. 14 | 15 | We take all security bugs seriously. Thank you for helping us improve the security of Copybara Action, we will make every effort to acknowledge your contributions. 16 | 17 | Please report security bugs in third-party modules to the person or team maintaining the module. 18 | 19 | ## Disclosure Policy 20 | 21 | When we receive a security bug report, we will assign it to a primary handler. This person will coordinate the fix and release process, involving the following steps: 22 | 23 | 1. Confirm the problem and determine the affected releases. 24 | 2. Audit code to find any potential similar problems. 25 | 3. Prepare fixes for all affected releases still under maintenance. 26 | 4. Review and release these fixes as fast as possible. 27 | 28 | ## Comments on this Policy 29 | 30 | If you have suggestions on how this process could be improved please submit a pull request. 31 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import * as core from "@actions/core"; 2 | import { CopybaraAction } from "./copybaraAction"; 3 | import { exit } from "./exit"; 4 | 5 | const action = new CopybaraAction({ 6 | // Credentials 7 | sshKey: core.getInput("ssh_key", { required: true }), 8 | accessToken: core.getInput("access_token"), 9 | 10 | // Common config 11 | sot: { 12 | repo: core.getInput("sot_repo"), 13 | branch: core.getInput("sot_branch"), 14 | }, 15 | destination: { 16 | repo: core.getInput("destination_repo"), 17 | branch: core.getInput("destination_branch"), 18 | }, 19 | committer: core.getInput("committer"), 20 | 21 | // Push config 22 | push: { 23 | include: core.getInput("push_include").split(" "), 24 | exclude: core.getInput("push_exclude").split(" "), 25 | move: core.getInput("push_move").split(/\r?\n/), 26 | replace: core.getInput("push_replace").split(/\r?\n/), 27 | }, 28 | 29 | // PR config 30 | pr: { 31 | include: core.getInput("pr_include").split(" "), 32 | exclude: core.getInput("pr_exclude").split(" "), 33 | move: core.getInput("pr_move").split(/\r?\n/), 34 | replace: core.getInput("pr_replace").split(/\r?\n/), 35 | }, 36 | 37 | // Advanced config 38 | customConfig: core.getInput("custom_config"), 39 | workflow: core.getInput("workflow"), 40 | copybaraOptions: core.getInput("copybara_options").split(" "), 41 | knownHosts: core.getInput("ssh_known_hosts"), 42 | prNumber: core.getInput("pr_number"), 43 | createRepo: core.getInput("create_repo") == "yes" ? true : false, 44 | 45 | // Docker 46 | image: { 47 | name: core.getInput("copybara_image"), 48 | tag: core.getInput("copybara_image_tag"), 49 | }, 50 | }); 51 | 52 | if (!core.isDebug()) { 53 | // Action fails gracefully on 'throw' 54 | process.on("unhandledRejection", (err) => exit(53, err as string)); 55 | action.run().then(exit).catch(exit); 56 | } else { 57 | core.debug("BEWARE: Debug mode is on, this could result in this action succeeding while it didn't. Check the logs."); 58 | action.run().then(exit); 59 | } 60 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Pull request 2 | 3 | 9 | 10 | ## Related issue 11 | 12 | 18 | 19 | ## Motivation and context 20 | 21 | 26 | 27 | ## Solution 28 | 29 | 33 | 34 | ## How has this been tested 35 | 36 | 42 | 43 | 44 | 45 | ## Types of changes 46 | 47 | 48 | 49 | - [ ] Bug fix (non-breaking change which fixes an issue) 50 | - [ ] New feature (non-breaking change which adds functionality) 51 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 52 | 53 | ## Checklist 54 | 55 | 56 | 57 | 58 | - [ ] My code follows the code style of this project. 59 | - [ ] My change requires a change to the documentation. 60 | - [ ] I have updated the documentation accordingly. 61 | - [ ] I have read the **docs/CONTRIBUTING.md** document. 62 | - [ ] I have added tests to cover my changes. 63 | - [ ] All new and existing tests passed. 64 | -------------------------------------------------------------------------------- /src/hostConfig.ts: -------------------------------------------------------------------------------- 1 | import { ensureFile, writeFile } from "fs-extra"; 2 | import { homedir } from "os"; 3 | 4 | export class hostConfig { 5 | static gitConfigPath = homedir() + "/.gitconfig"; 6 | static gitCredentialsPath = homedir() + "/.git-credentials"; 7 | static sshKeyPath = homedir() + "/.ssh/id_rsa"; 8 | static knownHostsPath = homedir() + "/.ssh/known_hosts"; 9 | static cbConfigPath = homedir() + "/copy.bara.sky"; 10 | static githubKnownHost = 11 | "github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk="; 12 | 13 | static async saveCommitter(committer: string): Promise { 14 | const match = committer.match(/^(.+)\s?<([^>]+)>/i); 15 | const committerName = match && match[1] ? match[1].trim() : "Github Actions"; 16 | const committerEmail = match && match[2] ? match[2].trim() : "actions@github.com"; 17 | 18 | return this.save( 19 | this.gitConfigPath, 20 | ` 21 | [user] 22 | name = ${committerName} 23 | email = ${committerEmail} 24 | ` 25 | ); 26 | } 27 | 28 | static async saveAccessToken(accessToken: string): Promise { 29 | return this.save(this.gitCredentialsPath, `https://user:${accessToken}@github.com`); 30 | } 31 | 32 | static async saveSshKey(sshKey: string): Promise { 33 | return this.save(this.sshKeyPath, sshKey); 34 | } 35 | 36 | static async saveKnownHosts(knownHosts: string): Promise { 37 | return this.save(this.knownHostsPath, `${this.githubKnownHost}\n${knownHosts}`); 38 | } 39 | 40 | static async saveCopybaraConfig(config: string): Promise { 41 | return this.save(this.cbConfigPath, config); 42 | } 43 | 44 | static async save(file: string, content: string): Promise { 45 | const filePath = file.replace("~", homedir()); 46 | return ensureFile(filePath).then(() => writeFile(filePath, content)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /.github/workflows/test.workflow.push.yml: -------------------------------------------------------------------------------- 1 | name: "Test 'Push' Workflow" 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | # same job as build.yml copied here to keep build & E2E workflows separate 10 | # Can be optimized once Github supports Actions partials 11 | build: 12 | name: Test build 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - name: Setup Node.js 18 | uses: actions/setup-node@v2 19 | with: 20 | node-version: 14 21 | 22 | - name: Install dependencies 23 | run: yarn install --frozen-lockfile 24 | 25 | - name: Build 26 | run: yarn build 27 | 28 | - name: Ensure build was committed (necessary for Github Actions) 29 | run: "[[ -z $(git status -s dist) ]]" 30 | 31 | - name: "[Test] Unit tests" 32 | run: yarn test 33 | 34 | e2e-test: 35 | name: "'Push' E2E tests" 36 | if: github.repository == 'mailchain/copybara-action' 37 | needs: [build] 38 | runs-on: ubuntu-latest 39 | steps: 40 | - name: Checkout 41 | uses: actions/checkout@v2 42 | with: 43 | fetch-depth: 0 44 | 45 | - name: "[Test] Basic usage" 46 | uses: ./ 47 | with: 48 | sot_repo: mailchain/copybara-action 49 | destination_repo: mailchain/copybara-action-test 50 | access_token: ${{ secrets.GH_TOKEN_BOT }} 51 | ssh_key: ${{ secrets.GH_SSH_BOT }} 52 | 53 | - name: "[Test] Missing repos" 54 | id: missing-repos 55 | continue-on-error: true 56 | uses: ./ 57 | with: 58 | destination_repo: mailchain/copybara-action-test 59 | ssh_key: ${{ secrets.GH_SSH_BOT }} 60 | workflow: push 61 | - name: ".........⏳" 62 | if: steps.missing-repos.outcome != 'failure' || !startsWith(steps.missing-repos.outputs.msg, '[action]') 63 | run: exit 1 64 | 65 | - name: "[Test] Missing token" 66 | id: missing-token 67 | continue-on-error: true 68 | uses: ./ 69 | with: 70 | sot_repo: mailchain/copybara-action 71 | destination_repo: mailchain/copybara-action-test 72 | ssh_key: ${{ secrets.GH_SSH_BOT }} 73 | - name: "..........⏳" 74 | if: steps.missing-token.outcome != 'failure' || !startsWith(steps.missing-token.outputs.msg, '[action]') 75 | run: exit 1 76 | -------------------------------------------------------------------------------- /src/copy.bara.sky.ts: -------------------------------------------------------------------------------- 1 | export const copyBaraSky = ( 2 | sotRepo: string, 3 | sotBranch: string, 4 | destinationRepo: string, 5 | destinationBranch: string, 6 | committer: string, 7 | localSot: string, 8 | pushInclude: string, 9 | pushExclude: string, 10 | pushTransformations: string, 11 | prInclude: string, 12 | prExclude: string, 13 | prTransformations: string 14 | ) => ` 15 | # Variables 16 | SOT_REPO = "${sotRepo}" 17 | SOT_BRANCH = "${sotBranch}" 18 | DESTINATION_REPO = "${destinationRepo}" 19 | DESTINATION_BRANCH = "${destinationBranch}" 20 | COMMITTER = "${committer}" 21 | LOCAL_SOT = "${localSot}" 22 | 23 | PUSH_INCLUDE = [${pushInclude}] 24 | PUSH_EXCLUDE = [${pushExclude}] 25 | PUSH_TRANSFORMATIONS = [${pushTransformations} 26 | ] 27 | 28 | PR_INCLUDE = [${prInclude}] 29 | PR_EXCLUDE = [${prExclude}] 30 | PR_TRANSFORMATIONS = [${prTransformations} 31 | ] 32 | 33 | # Push workflow 34 | core.workflow( 35 | name = "push", 36 | origin = git.origin( 37 | url = LOCAL_SOT if LOCAL_SOT else SOT_REPO, 38 | ref = SOT_BRANCH, 39 | ), 40 | destination = git.github_destination( 41 | url = DESTINATION_REPO, 42 | push = DESTINATION_BRANCH, 43 | ), 44 | origin_files = glob(PUSH_INCLUDE, exclude = PUSH_EXCLUDE), 45 | destination_files = glob(PR_INCLUDE if PR_INCLUDE else ["**"], exclude = PR_EXCLUDE), 46 | authoring = authoring.pass_thru(default = COMMITTER), 47 | mode = "ITERATIVE", 48 | transformations = [ 49 | metadata.restore_author("ORIGINAL_AUTHOR", search_all_changes = True), 50 | metadata.expose_label("COPYBARA_INTEGRATE_REVIEW"), 51 | ] + PUSH_TRANSFORMATIONS if PUSH_TRANSFORMATIONS else core.reverse(PR_TRANSFORMATIONS), 52 | ) 53 | 54 | # Pull Request workflow 55 | core.workflow( 56 | name = "pr", 57 | origin = git.github_pr_origin( 58 | url = DESTINATION_REPO, 59 | branch = DESTINATION_BRANCH, 60 | ), 61 | destination = git.github_pr_destination( 62 | url = SOT_REPO, 63 | destination_ref = SOT_BRANCH, 64 | integrates = [], 65 | ), 66 | destination_files = glob(PUSH_INCLUDE, exclude = PUSH_EXCLUDE), 67 | origin_files = glob(PR_INCLUDE if PR_INCLUDE else ["**"], exclude = PR_EXCLUDE), 68 | authoring = authoring.pass_thru(default = COMMITTER), 69 | mode = "CHANGE_REQUEST", 70 | set_rev_id = False, 71 | transformations = [ 72 | metadata.save_author("ORIGINAL_AUTHOR"), 73 | metadata.expose_label("GITHUB_PR_NUMBER", new_name = "Closes", separator = DESTINATION_REPO.replace("git@github.com:", " ").replace(".git", "#")), 74 | ] + PR_TRANSFORMATIONS, 75 | ) 76 | `; 77 | -------------------------------------------------------------------------------- /.github/workflows/copybara-docker.yml: -------------------------------------------------------------------------------- 1 | name: Publish Copybara Docker Image 2 | on: 3 | workflow_dispatch: 4 | schedule: 5 | - cron: "0 0 * * 0" # Weekly 6 | 7 | jobs: 8 | publish-copybara: 9 | if: github.repository == 'mailchain/copybara-action' 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: read 13 | packages: write 14 | id-token: write 15 | name: Publish to Docker Hub 16 | steps: 17 | - uses: actions/checkout@v2 18 | with: 19 | repository: google/copybara 20 | fetch-depth: 1 21 | 22 | - name: Get Copybara latest commit's SHA 23 | run: echo "COPYBARA_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_ENV 24 | 25 | - name: Extract metadata from GitHub Action context 26 | id: meta 27 | uses: docker/metadata-action@v3 28 | with: 29 | images: | 30 | mailchain/copybara 31 | ghcr.io/mailchain/copybara 32 | tags: | 33 | type=raw,${{ env.COPYBARA_SHA }} 34 | type=schedule,pattern={{date 'YYYYMMDD'}} 35 | flavor: | 36 | latest=true 37 | 38 | - name: Set up QEMU 39 | uses: docker/setup-qemu-action@v1 40 | 41 | - name: Set up Docker Buildx 42 | id: buildx 43 | uses: docker/setup-buildx-action@v1 44 | 45 | - name: Login to DockerHub 46 | uses: docker/login-action@v1 47 | with: 48 | username: mailchain 49 | password: ${{ secrets.DOCKER_TOKEN_BOT }} 50 | 51 | - name: Login to GitHub Container Registry 52 | uses: docker/login-action@v1 53 | with: 54 | registry: ghcr.io 55 | username: ${{ github.actor }} 56 | password: ${{ github.token }} 57 | 58 | # Remove when https://github.com/google/copybara/pull/186 is merged 59 | - name: Use working Dockerfile 60 | run: curl -s https://raw.githubusercontent.com/mailchain/copybara-action/main/.github/Dockerfile > Dockerfile 61 | 62 | - name: Build and push 63 | uses: docker/build-push-action@v2 64 | with: 65 | context: . 66 | platforms: linux/amd64,linux/arm64 67 | push: true 68 | tags: ${{ steps.meta.outputs.tags }} 69 | labels: ${{ steps.meta.outputs.labels }} 70 | 71 | # Experimental 72 | - name: Install Cosign 73 | uses: sigstore/cosign-installer@main 74 | - name: Sign the image with GitHub OIDC **EXPERIMENTAL** 75 | run: echo y | cosign sign --oidc-issuer https://token.actions.githubusercontent.com ${TAGS} 76 | env: 77 | COSIGN_EXPERIMENTAL: 1 78 | TAGS: ${{ steps.meta.outputs.tags }} 79 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "copybara-action", 3 | "version": "0.0.0", 4 | "private": true, 5 | "description": "Transform and move code between repositories.", 6 | "main": "src/main.ts", 7 | "scripts": { 8 | "build": "ncc build --source-map --license licenses.txt", 9 | "build:check": "yarn build && [[ -z $(git status -s dist) ]]", 10 | "format": "prettier --write **/*.ts", 11 | "format:check": "prettier --check **/*.ts", 12 | "lint": "eslint --cache --fix src/**/*.ts", 13 | "test": "jest --passWithNoTests", 14 | "test:local": "act --secret-file=.secrets", 15 | "test:local:watch": "yarn test:action --watch" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/mailchain/copybara-action.git" 20 | }, 21 | "keywords": [ 22 | "actions", 23 | "copybara" 24 | ], 25 | "author": "", 26 | "license": "MIT", 27 | "dependencies": { 28 | "@actions/artifact": "^0.3.5", 29 | "@actions/core": "^1.2.5", 30 | "@actions/exec": "^1.0.4", 31 | "@actions/github": "^4.0.0", 32 | "fs-extra": "^9.0.1" 33 | }, 34 | "devDependencies": { 35 | "@commitlint/cli": "^11.0.0", 36 | "@commitlint/config-conventional": "^11.0.0", 37 | "@semantic-release/changelog": "^5.0.1", 38 | "@semantic-release/commit-analyzer": "^8.0.1", 39 | "@semantic-release/exec": "^5.0.0", 40 | "@semantic-release/git": "^9.0.0", 41 | "@semantic-release/github": "^7.1.1", 42 | "@semantic-release/release-notes-generator": "^9.0.1", 43 | "@types/fs-extra": "^9.0.1", 44 | "@types/jest": "^26.0.10", 45 | "@types/node": "^14.6.0", 46 | "@typescript-eslint/parser": "^3.10.1", 47 | "@vercel/ncc": "^0.23.0", 48 | "commitizen": "^4.2.1", 49 | "cz-customizable": "^6.3.0", 50 | "eslint": "^7.7.0", 51 | "eslint-plugin-github": "^4.1.1", 52 | "eslint-plugin-jest": "^23.20.0", 53 | "eslint-plugin-node": "^11.1.0", 54 | "eslint-plugin-prettier": "^3.1.4", 55 | "husky": "^4.3.0", 56 | "jest": "^24.9.0", 57 | "jest-circus": "^26.4.2", 58 | "js-yaml": "^3.14.0", 59 | "lint-staged": ">=10", 60 | "prettier": "2.1.1", 61 | "tablemark": "^2.0.0", 62 | "ts-jest": "^24.3.0", 63 | "typescript": "^4.0.2", 64 | "yaml": "^1.10.0" 65 | }, 66 | "config": { 67 | "commitizen": { 68 | "path": "cz-customizable" 69 | } 70 | }, 71 | "husky": { 72 | "hooks": { 73 | "prepare-commit-msg": "exec < /dev/tty && git-cz --hook || true", 74 | "commit-msg": "echo $HUSKY_GIT_PARAMS && echo $HUSKY_GIT_STDIN && commitlint -E HUSKY_GIT_PARAMS --config .config/commitlint.config.js", 75 | "pre-commit": "lint-staged && ./.config/commit-docs.sh && ./.config/commit-build.sh" 76 | } 77 | }, 78 | "lint-staged": { 79 | "src/**/*.ts": "eslint --cache --fix" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/exit.ts: -------------------------------------------------------------------------------- 1 | import * as core from "@actions/core"; 2 | 3 | export const exitCodes: { 4 | [k: number]: ExitCode; 5 | } = { 6 | 0: { 7 | msg: "Everything went well and the migration was successful.", 8 | ns: "copybara", 9 | type: "success", 10 | }, 11 | 1: { 12 | msg: "Error parsing the command line. Check the logs for details.", 13 | ns: "copybara", 14 | type: "error", 15 | }, 16 | 2: { 17 | msg: 18 | "Error in the configuration, flags values or in general an error attributable to the user. Check the logs for details.", 19 | ns: "copybara", 20 | type: "error", 21 | }, 22 | 3: { 23 | msg: "Error during repository manipulation. Check the logs for details.", 24 | ns: "copybara", 25 | type: "error", 26 | }, 27 | 4: { 28 | msg: "Execution resulted in no-op, which means that no changes were made in the destination.", 29 | ns: "copybara", 30 | type: "warning", 31 | }, 32 | 8: { 33 | msg: "Execution was interrupted. Check the logs for details.", 34 | ns: "copybara", 35 | type: "error", 36 | }, 37 | 30: { 38 | msg: "Error accessing the network, filesystem errors, etc. Check the logs for details.", 39 | ns: "copybara", 40 | type: "error", 41 | }, 42 | 31: { 43 | msg: "Unexpected error. This would be a Copybara bug. Check the logs for details.", 44 | ns: "copybara", 45 | type: "error", 46 | }, 47 | 50: { 48 | msg: "Action completed successfully.", 49 | ns: "action", 50 | type: "success", 51 | }, 52 | 51: { 53 | msg: "Error with an input variable. Check the logs for details.", 54 | ns: "action", 55 | type: "error", 56 | }, 57 | 52: { 58 | msg: "Unexpected error running Copybara. Please open an issue on olivr/copybara-action.", 59 | ns: "action", 60 | type: "error", 61 | }, 62 | 53: { 63 | msg: "Unexpected error. Please open an issue on olivr/copybara-action.", 64 | ns: "action", 65 | type: "error", 66 | }, 67 | 54: { 68 | msg: "Nothing to do.", 69 | ns: "action", 70 | type: "warning", 71 | }, 72 | }; 73 | 74 | type ExitCode = { 75 | type: "success" | "warning" | "error"; 76 | ns: "copybara" | "action"; 77 | msg: string; 78 | }; 79 | 80 | // Exit action 81 | export function exit(exitCode: number, message = "") { 82 | const ec = Object.prototype.hasOwnProperty.call(exitCodes, exitCode) ? exitCodes[exitCode] : exitCodes[53]; 83 | 84 | const msg = `[${ec.ns}] ${exitCode}: ${message ? message : ec.msg}`; 85 | core.setOutput("msg", msg); 86 | 87 | switch (ec.type) { 88 | case "success": 89 | core.info(msg); 90 | process.exit(0); // eslint-disable-line no-process-exit 91 | 92 | case "warning": 93 | core.warning(msg); 94 | process.exit(0); // eslint-disable-line no-process-exit 95 | 96 | default: 97 | core.setFailed(msg); 98 | process.exit(1); // eslint-disable-line no-process-exit 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /docs/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | - Using welcoming and inclusive language 18 | - Being respectful of differing viewpoints and experiences 19 | - Gracefully accepting constructive criticism 20 | - Focusing on what is best for the community 21 | - Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | - The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | - Trolling, insulting/derogatory comments, and personal or political attacks 28 | - Public or private harassment 29 | - Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | - Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at {{ cookiecutter.author_email }}. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see the [Contributor Covenant FAQ][faq] 76 | 77 | [faq]: https://www.contributor-covenant.org/faq 78 | -------------------------------------------------------------------------------- /docs/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | > ⚠️ If you are just looking for help, see [support](SUPPORT.md) 4 | 5 | First off, thank you for taking the time to contribute! 👍 6 | 7 | The following is a set of guidelines for contributing. 8 | These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document. 9 | 10 | Please note we have a [code of conduct](CODE_OF_CONDUCT.md), please follow it in all your interactions with Copybara Action. 11 | 12 | ## [Developer documentation](development.md) 13 | 14 | ## Five other ways to contribute 15 | 16 | ⭐ Star this repo: it's quick and goes a long way! [🔝](#top) 17 | 18 | 🗣️ [Spread the word](#spread-the-word) 19 | 20 | 🐞 [Report bugs](#report-bugs) 21 | 22 | ✅ [Resolve issues](#resolve-issues) 23 | 24 | 📝 [Improve the documentation](#improve-the-documentation) 25 | 26 | ### Spread the Word 27 | 28 | If you like Copybara Action, you can bring it up in a conversation at the coffee machine with your colleagues, on an internet forum, Hacker News, StackOverflow, Reddit, Quora, Linkedin, etc. 29 | 30 | If you own a blog or are thinking of starting one, Copybara Action and how you use it might be a good subject for an article. 31 | 32 | If you are using Copybara Action in one way or another, credits are always welcome. 33 | 34 | ### Report Bugs 35 | 36 | If you found a bug or want to propose a new feature, thank you for taking the time to report it 🙏 37 | To do so, just open an issue and give as much information as you can about the bug you found and how to reproduce it. 38 | 39 | > ⚠️ If you found a vulnerability, please see [our security policy](SECURITY.md) 40 | 41 | ### Resolve Issues 42 | 43 | If you are new to Copybara Action and are not sure where to start, we recommend that you look for issues labelled **good first issue** or **easy** 44 | 45 | Before coding your pull request, please first discuss the change you wish to make by commenting on an existing issue or by opening an issue. 46 | 47 | #### Pull Request Process 48 | 49 | We follow the [GitHub Flow](https://guides.github.com/introduction/flow/). 50 | 51 | [![GitHub Flow](docs/images/gitflow.png "GitHub Flow")](https://guides.github.com/pdfs/githubflow-online.pdf) 52 | 53 | Here is the process: 54 | 55 | 1. Fork this repo 56 | 57 | 2. Clone **your fork** on your development machine 58 | 59 | 3. Create a branch named after the feature you're working on 60 | 61 | ```sh 62 | git checkout -b 63 | ``` 64 | 65 | 4. Write code and tests for your change then commit them to your branch and push them to your repo. 66 | 67 | ```sh 68 | git add . 69 | git commit 70 | git push origin 71 | ``` 72 | 73 | **How to write a descriptive commit message**: 74 | 75 | - Describe what was done; not the result 76 | - Use the active voice 77 | - Use the present tense 78 | - Capitalize properly 79 | - Do not end in a period — this is a title/subject 80 | - Prefix the subject with its scope 81 | 82 | 5. Open a pull request against Copybara Action 83 | 84 | 6. Work with the project maintainer(s) to get your pull request reviewed 85 | 86 | 7. Wait for your pull request to be merged and watch it to answer any questions or make any changes you're asked. You will make these modifications directly on your feature branch. 87 | 88 | 8. Once it is merged, you can delete your feature branch 89 | 90 | ```sh 91 | git branch -d 92 | git push origin --delete 93 | ``` 94 | 95 | ### Improve the Documentation 96 | 97 | We also welcome improvements to the documentation of Copybara Action. If you want to fix a typo, add a sentence or two, an example, etc. we recommend that you open a [pull request](#pull-request-process) directly. For bigger changes to the documentation, we recommend to [discuss it in an issue](#report-bugs) before spending any time making huge changes that could be rejected. 98 | 99 | ## License 100 | 101 | By contributing your code, you agree to license your contribution under the 102 | terms of our [LICENSE](../LICENSE) 103 | 104 | ## Code of Conduct 105 | 106 | Read our [Code of Conduct](CODE_OF_CONDUCT.md) for Copybara Action. 107 | -------------------------------------------------------------------------------- /docs/advanced-usage.md: -------------------------------------------------------------------------------- 1 | # Advanced usage 2 | 3 | ## What this action does 4 | 5 | This action manages 2 workflows out of the box (**push**, **pr**) and essentially has three steps: 6 | 7 | 1. **Detect** which workflow to run depending on the context: 8 | 9 | - _Push to SoT_ => **push** 10 | - _PR opened/updated on destination_ => **pr** 11 | 12 | > If the destination branch doesn't exist on **push**, it will add the flags `--force --init-history` 13 | 14 | 2. **Generate** a configuration for each workflow 15 | 16 | 3. **Run** Copybara 17 | 18 | ## What you can do 19 | 20 | ### Force specific workflows 21 | 22 | _The following will let you override step 1 but let Copybara Action perform step 2 and 3._ 23 | 24 | It will ensure the **push** workflow is ran using the auto-generated configuration file. 25 | 26 | ```yaml 27 | on: 28 | push: 29 | jobs: 30 | move-code: 31 | runs-on: ubuntu-latest 32 | steps: 33 | - uses: actions/checkout@v2 34 | with: 35 | fetch-depth: 0 36 | 37 | - uses: mailchain/copybara-action@v1.2.3 38 | with: 39 | ssh_key: ${{ secrets.SSH_KEY }} 40 | sot_repo: your/repo 41 | destination_repo: other/repo 42 | workflow: push 43 | ``` 44 | 45 | ### Custom Copybara configuration 46 | 47 | _The following will let Copybara Action perform step 1 and 3 but let you override step 2._ 48 | 49 | It will use your configuration instead of the automatically generated one. 50 | You can get inspiration from the [generated config](#generated-config) to get started. 51 | 52 | > This example is still making use of the workflow detection so `my.own.copy.bara.sky` must contain a **push** and a **pr** workflow 53 | 54 | ```yaml 55 | on: 56 | push: 57 | pull_request_target: 58 | jobs: 59 | move-code: 60 | runs-on: ubuntu-latest 61 | steps: 62 | - uses: actions/checkout@v2 63 | with: 64 | fetch-depth: 0 65 | 66 | - uses: mailchain/copybara-action@v1.2.3 67 | with: 68 | access_token: ${{ secrets.GH_TOKEN }} 69 | ssh_key: ${{ secrets.SSH_KEY }} 70 | sot_repo: your/repo 71 | destination_repo: other/repo 72 | custom_config: my.own.copy.bara.sky 73 | ``` 74 | 75 | ### Completely custom 76 | 77 | _The following will let you override step 1, 2 and let you customize how Copybara Action will perform step 3._ 78 | 79 | It doesn't make use of any of our helpers for detecting the workflow and generating the configuration. 80 | It is even using a custom Copybara docker image! 81 | 82 | ```yaml 83 | on: 84 | push: 85 | jobs: 86 | move-code: 87 | runs-on: ubuntu-latest 88 | steps: 89 | - uses: actions/checkout@v2 90 | with: 91 | fetch-depth: 0 92 | 93 | - uses: mailchain/copybara-action@v1.2.3 94 | with: 95 | access_token: ${{ secrets.GH_TOKEN }} 96 | ssh_key: ${{ secrets.SSH_KEY }} 97 | workflow: create 98 | custom_config: my.own.copy.bara.sky 99 | copybara_image: sharelatex/copybara 100 | copybara_options: --force --init-history 101 | ``` 102 | 103 | ### SoT not on GitHub 104 | 105 | This action's auto-generated configuration was made for a use case with two GitHub repos. However, if your SoT is not on GitHub, this action is still helpful. 106 | Use it on your destination with a custom config and a specific workflow: 107 | 108 | ```yaml 109 | on: 110 | pull_request_target: 111 | jobs: 112 | move-code: 113 | runs-on: ubuntu-latest 114 | steps: 115 | - uses: mailchain/copybara-action@v1.2.3 116 | with: 117 | ssh_key: ${{ secrets.SSH_KEY }} 118 | workflow: pr 119 | custom_config: my.own.copy.bara.sky 120 | ``` 121 | 122 | ### [More options](inputs.md) 123 | 124 | ## Generated config 125 | 126 | So you don't have to start your custom config from scratch, you can check out the [config template](/src/copy.bara.sky.ts) used by this action or even better, you can download the one generated for your use case. 127 | 128 | 1. In your repo(s), add the secret `ACTIONS_STEP_DEBUG` and set it to `true` 129 | 2. Run the action workflow (ie. push a commit to SoT and create a PR in destination) 130 | 3. Check out the artifacts for your action workflow runs 131 | 4. **Delete the `ACTIONS_STEP_DEBUG` secret or set it to `false`** (or you may get false positives) 132 | 133 | It will look like this: 134 | ![artifact](images/artifact.png) 135 | -------------------------------------------------------------------------------- /docs/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### [1.2.3](https://github.com/olivr/copybara-action/compare/v1.2.2...v1.2.3) (2021-07-16) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * Init destination repo even when push is specified as a workflow ([6b2860c](https://github.com/olivr/copybara-action/commit/6b2860c82d71666e23ed343e8c0714cce316df00)) 7 | 8 | ### [1.2.2](https://github.com/olivr/copybara-action/compare/v1.2.1...v1.2.2) (2020-10-01) 9 | 10 | 11 | ### Bug Fixes 12 | 13 | * Update action's description ([af07ab1](https://github.com/olivr/copybara-action/commit/af07ab16220e1995ade74b00f3eac590e866b5c2)) 14 | 15 | ### [1.2.1](https://github.com/olivr/copybara-action/compare/v1.2.0...v1.2.1) (2020-10-01) 16 | 17 | 18 | ### Bug Fixes 19 | 20 | * Readme must be in root to publish Github Action ([bda8bec](https://github.com/olivr/copybara-action/commit/bda8bece34f140b3b5105bb2aba26d50aa9a1287)) 21 | 22 | ## [1.2.0](https://github.com/olivr/copybara-action/compare/v1.1.2...v1.2.0) (2020-10-01) 23 | 24 | 25 | ### ⚠ BREAKING CHANGES 26 | 27 | * Several inputs and their formats have changed 28 | 29 | ### Features 30 | 31 | * Add move and replace transformations ([e14a1e1](https://github.com/olivr/copybara-action/commit/e14a1e1f2f03313e185a7ae1074ff1d8773fff40)) 32 | 33 | 34 | ### Bug Fixes 35 | 36 | * Fix PR flow on pull_request_target ([ee5bd2a](https://github.com/olivr/copybara-action/commit/ee5bd2a5a42145b9200eed98134c8ba4219fd22b)) 37 | * Fix PR flow on pull_request_target ([4049d86](https://github.com/olivr/copybara-action/commit/4049d86a6564a5e5ac451706590e61674d83151d)) 38 | * Fix PR flow on pull_request_target ([35aa552](https://github.com/olivr/copybara-action/commit/35aa55297ccd219f1829bb12cef8919bbd60a46d)) 39 | * In/exclude_files should play well with make_root_path ([9f1d025](https://github.com/olivr/copybara-action/commit/9f1d025a53044f39db9f2a8ff258f4b86602f38c)) 40 | 41 | 42 | * Changed structure of inputs and internal code ([095d67b](https://github.com/olivr/copybara-action/commit/095d67b992915d34076a2956fbcca633f9e9596a)) 43 | 44 | ### [1.1.2](https://github.com/olivr/copybara-action/compare/v1.1.1...v1.1.2) (2020-09-24) 45 | 46 | 47 | ### ⚠ BREAKING CHANGES 48 | 49 | * The input `copybara_options` is now taking space-separated options 50 | 51 | ### Bug Fixes 52 | 53 | * Changed some options ([f9be6b4](https://github.com/olivr/copybara-action/commit/f9be6b4018c83c6c5e78a713a3df9dc4f79d4202)) 54 | 55 | ### [1.1.1](https://github.com/olivr/copybara-action/compare/v1.1.0...v1.1.1) (2020-09-22) 56 | 57 | ## [1.1.0](https://github.com/olivr/copybara-action/compare/v1.0.5...v1.1.0) (2020-09-22) 58 | 59 | 60 | ### Features 61 | 62 | * Add custom config input ([a4d2886](https://github.com/olivr/copybara-action/commit/a4d288690526d10e45631ad77121276a9667e6ad)) 63 | 64 | ### [1.0.5](https://github.com/olivr/copybara-action/compare/v1.0.4...v1.0.5) (2020-09-19) 65 | 66 | 67 | ### Bug Fixes 68 | 69 | * Shell command for automatic version update was buggy ([5c92ba5](https://github.com/olivr/copybara-action/commit/5c92ba557bc6c630a75ba0a5a6b0b49b51fd9a7a)) 70 | 71 | ### [1.0.4](https://github.com/olivr/copybara-action/compare/v1.0.3...v1.0.4) (2020-09-19) 72 | 73 | 74 | ### Bug Fixes 75 | 76 | * Automatic version update was in wrong semantic-release step ([a06e19a](https://github.com/olivr/copybara-action/commit/a06e19a0704758cfce5baa0c92d430efb693078c)) 77 | 78 | ### [1.0.3](https://github.com/olivr/copybara-action/compare/v1.0.2...v1.0.3) (2020-09-19) 79 | 80 | 81 | ### Bug Fixes 82 | 83 | * Fix automatic version update in documentation ([bbaedfd](https://github.com/olivr/copybara-action/commit/bbaedfde873cc8e8d92a1ee8710dc6d16b6a8ac8)) 84 | 85 | ### [1.0.2](https://github.com/olivr/copybara-action/compare/v1.0.1...v1.0.2) (2020-09-19) 86 | 87 | 88 | ### Bug Fixes 89 | 90 | * Update action's version automatically in documentation ([060635e](https://github.com/olivr/copybara-action/commit/060635edbefc9c4225db654791d6a306f4fdfa7b)) 91 | 92 | ### [1.0.1](https://github.com/olivr/copybara-action/compare/v1.0.0...v1.0.1) (2020-09-19) 93 | 94 | 95 | ### Bug Fixes 96 | 97 | * Update action's version automatically in documentation ([67d5d8a](https://github.com/olivr/copybara-action/commit/67d5d8a487cf83fb507c410fbeb35451610a72da)) 98 | 99 | ## 1.0.0 (2020-09-18) 100 | 101 | ### ⚠ BREAKING CHANGES 102 | 103 | - Release 1st version 104 | 105 | ### Bug Fixes 106 | 107 | - Add correct repo URL ([6f31c12](https://github.com/olivr/copybara-action/commit/6f31c12fe1b20c92f825da8e9548ce8b644bbca4)) 108 | -------------------------------------------------------------------------------- /.github/workflows/test.workflow.pr.yml: -------------------------------------------------------------------------------- 1 | name: "Test 'PR' Workflow" 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | # When new code is pushed on destination, create a PR to trigger an E2E test of the PR workflow 11 | prepare-test: 12 | name: "[Chore] Create Test Pull Request" 13 | if: github.repository == 'mailchain/copybara-action-test' && github.event_name == 'push' 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v2 18 | 19 | - name: Get Commit Message 20 | id: commit 21 | run: | 22 | MSG=$(git log --format=%B -n 1 ${{ github.event.after }}) 23 | echo "::set-output name=COMMIT_MESSAGE::${MSG}" 24 | 25 | - name: Toggle test file 26 | run: '[ -e "./.test-pr-workflow" ] && rm -rf ./.test-pr-workflow || echo "${{ github.sha }}" > ./.test-pr-workflow' 27 | 28 | - name: Create Pull Request 29 | uses: peter-evans/create-pull-request@v3 30 | env: 31 | COMMIT_MESSAGE: ${{ steps.commit.outputs.COMMIT_MESSAGE }} 32 | with: 33 | commit-message: "[Test PR] ${{env.COMMIT_MESSAGE}}" 34 | title: "[Test PR] ${{env.COMMIT_MESSAGE}}" 35 | body: "Generated by workflow [${{ github.run_id }}](../actions/runs/${{ github.run_id }})" 36 | token: ${{ secrets.GH_TOKEN_BOT }} 37 | branch: test-pr-workflow 38 | branch-suffix: short-commit-hash 39 | 40 | # When a new test PR is created on destination (by the job above), copy it to SoT 41 | e2e-test: 42 | name: "'PR' E2E tests" 43 | if: github.repository == 'mailchain/copybara-action-test' && github.event_name == 'pull_request' && startsWith(github.event.pull_request.title, '[Test PR]') 44 | runs-on: ubuntu-latest 45 | steps: 46 | - name: Checkout 47 | uses: actions/checkout@v2 48 | 49 | - name: "[Test] Basic usage" 50 | uses: ./ 51 | with: 52 | sot_repo: mailchain/copybara-action 53 | destination_repo: mailchain/copybara-action-test 54 | access_token: ${{ secrets.GH_TOKEN_BOT }} 55 | ssh_key: ${{ secrets.GH_SSH_BOT }} 56 | 57 | - name: "🧹 Close Pull Request" 58 | uses: actions/github-script@v3 59 | with: 60 | github-token: ${{ secrets.GH_TOKEN_BOT }} 61 | script: | 62 | github.pulls.update({ 63 | owner: context.repo.owner, 64 | repo: context.repo.repo, 65 | pull_number: context.payload.pull_request.number, 66 | state: 'closed' 67 | }); 68 | 69 | - name: "🧹 Delete PR branch" 70 | run: git push origin :${{github.head_ref}} 71 | 72 | # When a new test PR is created on SoT (by the job above), close it 73 | cleanup-test: 74 | name: "[Chore] Close Test Pull Request" 75 | if: github.repository == 'mailchain/copybara-action' && github.event_name == 'pull_request' && startsWith(github.event.pull_request.title, '[Test PR]') 76 | runs-on: ubuntu-latest 77 | steps: 78 | - name: Checkout 79 | uses: actions/checkout@v2 80 | 81 | - name: "🧹 Close Pull Request" 82 | uses: actions/github-script@v3 83 | with: 84 | github-token: ${{ secrets.GH_TOKEN_BOT }} 85 | script: | 86 | github.pulls.update({ 87 | owner: context.repo.owner, 88 | repo: context.repo.repo, 89 | pull_number: context.payload.pull_request.number, 90 | state: 'closed' 91 | }); 92 | 93 | - name: "🧹 Delete PR branch" 94 | run: git push origin :${{github.head_ref}} 95 | 96 | # Once the test PR is cleaned up by the job above, it means all the tests have passed and we can release new code 97 | release: 98 | name: Release new version 99 | needs: [cleanup-test] 100 | runs-on: ubuntu-18.04 101 | steps: 102 | - name: Checkout 103 | uses: actions/checkout@master 104 | with: 105 | ref: main 106 | 107 | - name: Setup Node.js 108 | uses: actions/setup-node@v2 109 | with: 110 | node-version: 14 111 | 112 | - name: Install dependencies 113 | run: yarn install --frozen-lockfile 114 | 115 | - name: Release 116 | env: 117 | GITHUB_TOKEN: ${{ secrets.GH_TOKEN_BOT }} 118 | # Trick semantic release because releasing from a PR is unusual but necessary in our case 119 | run: GITHUB_EVENT_NAME=push GITHUB_REF=main npx semantic-release -e ./.config/release.config.js 120 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: "Copybara Action - Mailchain" 2 | description: "Transform and move code between repositories. Start with ZERO config and 100% customizable." 3 | author: "Romain Barissat" 4 | 5 | inputs: 6 | ssh_key: 7 | description: "SSH public key." 8 | required: true 9 | 10 | access_token: 11 | description: "Personal access token with `repo` permissions. Always required on *destination*. Required on *SoT* only if the variable `sot_branch` below is left empty and to decide between **push** and **init** workflows." 12 | required: false 13 | 14 | sot_repo: 15 | description: "Source repository (Source of Truth)." 16 | required: false 17 | 18 | sot_branch: 19 | description: "SoT branch. Defaults to your repository's default branch." 20 | required: false 21 | default: "" 22 | 23 | destination_repo: 24 | description: "Destination repository." 25 | required: false 26 | 27 | destination_branch: 28 | description: "Destination branch. Defaults to the same as your SoT's branch name." 29 | required: false 30 | default: "" 31 | 32 | push_include: 33 | description: "Files to include when pushing from SoT => Destination (space separated globs). Defaults to all files." 34 | required: false 35 | default: "**" 36 | 37 | push_exclude: 38 | description: "Files to exclude when pushing from SoT => Destination (space separated globs). Defaults to none." 39 | required: false 40 | default: "" 41 | 42 | push_move: 43 | description: "Files to move before pushing from SoT => Destination. In the format `from||to||match` where `match` is a glob filter to match only specific files within `from` (defaults to all). Separate each move operation by a line return. Defaults to reverse of `pr_move`. `push_move` is always run before `push_replace`." 44 | required: false 45 | default: "" 46 | 47 | push_replace: 48 | description: "Files to replace before pushing from SoT => Destination. In the format `search||replace||match` where `match` is a glob filter to search only those files (defaults to all). Separate each replace operation by a line return. Defaults to reverse of `pr_replace`. `push_replace` is always run after `push_move`." 49 | required: false 50 | default: "" 51 | 52 | pr_include: 53 | description: "Files to include when pulling from Destination => SoT (space separated globs). Defaults to all files." 54 | required: false 55 | default: "**" 56 | 57 | pr_exclude: 58 | description: "Files to exclude when pulling from Destination => SoT (space separated globs). Defaults to none." 59 | required: false 60 | default: "" 61 | 62 | pr_move: 63 | description: "Files to move before pushing from Destination => SoT. In the format `from||to||match` where `match` is a glob filter to match only specific files within `from` (defaults to all). Separate each move operation by a line return. Defaults to none. `pr_move` is always run after `pr_replace`." 64 | required: false 65 | default: "" 66 | 67 | pr_replace: 68 | description: "Files to replace before pushing from Destination => SoT. In the format `search||replace||match` where `match` is a glob filter to search only those files (defaults to all). Separate each replace operation by a line return. Defaults to none. `pr_replace` is always run before `pr_move`." 69 | required: false 70 | default: "" 71 | 72 | committer: 73 | description: "Who will commit changes." 74 | required: false 75 | default: Github Actions 76 | 77 | custom_config: 78 | description: "Copybara custom configuration file to use. Using this will ignore all the pr_* and push_* inputs." 79 | required: false 80 | default: "" 81 | 82 | workflow: 83 | description: "Workflow to execute. Defaults to auto-detect (init / push / pr)." 84 | required: false 85 | default: "" 86 | 87 | copybara_options: 88 | description: "Use this, if you want to manually specify some command line options (space-separated)." 89 | required: false 90 | default: "" 91 | 92 | ssh_known_hosts: 93 | description: "SSH known hosts file contents, for authenticating with Copybara with another Git server. GitHub is always included by default." 94 | required: false 95 | default: "" 96 | 97 | copybara_image: 98 | description: "Copybara Docker image to run." 99 | required: false 100 | default: "mailchain/copybara" 101 | 102 | copybara_image_tag: 103 | description: "Copybara Docker image tag to use." 104 | required: false 105 | default: "latest" 106 | 107 | pr_number: 108 | description: "If you manually specified the 'pr' workflow, you will need to specify the PR number as well." 109 | required: false 110 | default: "" 111 | 112 | create_repo: 113 | description: "If the destination repo doesn't exist, it will be created (subject to enough permissions attached to the access token)." 114 | required: false 115 | default: "yes" 116 | 117 | runs: 118 | using: "node12" 119 | main: "dist/index.js" 120 | 121 | branding: 122 | icon: "copy" 123 | color: "purple" 124 | -------------------------------------------------------------------------------- /src/copybara.ts: -------------------------------------------------------------------------------- 1 | import { copyBaraSky } from "./copy.bara.sky"; 2 | import { exec } from "@actions/exec"; 3 | import { exitCodes } from "./exit"; 4 | import { hostConfig } from "./hostConfig"; 5 | 6 | export class CopyBara { 7 | constructor(readonly image: DockerConfig) {} 8 | 9 | public async download(): Promise { 10 | return exec("docker", ["pull", `${this.image.name}:${this.image.tag}`]); 11 | } 12 | 13 | public async run(workflow: string, copybaraOptions: string[], ref: string | number = ""): Promise { 14 | switch (workflow) { 15 | case "init": 16 | return this.exec( 17 | ["-e", "COPYBARA_WORKFLOW=push"], 18 | ["--force", "--init-history", "--ignore-noop", ...copybaraOptions] 19 | ); 20 | 21 | case "pr": 22 | return this.exec( 23 | ["-e", "COPYBARA_WORKFLOW=pr", "-e", `COPYBARA_SOURCEREF=${ref}`], 24 | ["--ignore-noop", ...copybaraOptions] 25 | ); 26 | 27 | default: 28 | return this.exec(["-e", `COPYBARA_WORKFLOW=${workflow}`], ["--ignore-noop", ...copybaraOptions]); 29 | } 30 | } 31 | 32 | public static getConfig(workflow: string, config: CopybaraConfig): string { 33 | this.validateConfig(config, workflow); 34 | return copyBaraSky( 35 | `git@github.com:${config.sot.repo}.git`, 36 | config.sot.branch, 37 | `git@github.com:${config.destination.repo}.git`, 38 | config.destination.branch, 39 | config.committer, 40 | "file:///usr/src/app", 41 | this.generateInExcludes(config.push.include), 42 | this.generateInExcludes(config.push.exclude), 43 | this.generateTransformations(config.push.move, config.push.replace, "push"), 44 | this.generateInExcludes(config.pr.include), 45 | this.generateInExcludes(config.pr.exclude), 46 | this.generateTransformations(config.pr.move, config.pr.replace, "pr") 47 | ); 48 | } 49 | 50 | private async exec(dockerParams: string[] = [], copybaraOptions: string[] = []): Promise { 51 | const cbOptions = !copybaraOptions.length ? [] : [`-e`, `COPYBARA_OPTIONS`]; 52 | 53 | const execExitCode = await exec( 54 | `docker`, 55 | [ 56 | "run", 57 | `-v`, 58 | `${process.cwd()}:/usr/src/app`, 59 | `-v`, 60 | `${hostConfig.sshKeyPath}:/root/.ssh/id_rsa`, 61 | `-v`, 62 | `${hostConfig.knownHostsPath}:/root/.ssh/known_hosts`, 63 | `-v`, 64 | `${hostConfig.cbConfigPath}:/root/copy.bara.sky`, 65 | `-v`, 66 | `${hostConfig.gitConfigPath}:/root/.gitconfig`, 67 | `-v`, 68 | `${hostConfig.gitCredentialsPath}:/root/.git-credentials`, 69 | `-e`, 70 | `COPYBARA_CONFIG=/root/copy.bara.sky`, 71 | ...dockerParams, 72 | ...cbOptions, 73 | this.image.name, 74 | "copybara", 75 | ], 76 | { 77 | ignoreReturnCode: true, 78 | env: { COPYBARA_OPTIONS: copybaraOptions.join(" ") }, 79 | } 80 | ); 81 | 82 | const exitCode = exitCodes[execExitCode]; 83 | 84 | if (exitCode && exitCode.ns == "copybara") { 85 | // success/warning 86 | if (exitCode.type == "success" || exitCode.type == "warning") return execExitCode; 87 | // known errors 88 | else throw execExitCode; 89 | } // unknown error 90 | else throw 52; 91 | } 92 | 93 | private static validateConfig(config: CopybaraConfig, workflow: string) { 94 | if (!config.committer) throw 'You need to set a value for "committer".'; 95 | if (!config.image.name) throw 'You need to set a value for "copybara_image".'; 96 | if (!config.image.tag) throw 'You need to set a value for "copybara_image_tag".'; 97 | if (workflow == "push" && !config.push.include.length) throw 'You need to set a value for "push_include".'; 98 | if (workflow == "pr" && !config.pr.include.length) throw 'You need to set a value for "pr_include".'; 99 | if (!config.sot.repo || !config.destination.repo) 100 | throw 'You need to set values for "sot_repo" & "destination_repo" or set a value for "custom_config".'; 101 | } 102 | 103 | private static generateInExcludes(inExcludesArray: string[]) { 104 | const inExcludeGlobs = inExcludesArray.filter((v) => v); 105 | let inExcludeString = ""; 106 | 107 | if (inExcludeGlobs.length) inExcludeString = `"${inExcludeGlobs.join('","')}"`; 108 | return inExcludeString; 109 | } 110 | 111 | private static generateTransformations(moves: string[], replacements: string[], type: "push" | "pr") { 112 | const move = this.transformer(moves, "move"); 113 | const replace = this.transformer(replacements, "replace"); 114 | 115 | return type == "push" 116 | ? // Move first then replace for push 117 | move.concat(replace) 118 | : // Replace first then move for PR 119 | replace.concat(move); 120 | } 121 | 122 | private static transformer(list: string[], method: string) { 123 | let transformation = ""; 124 | 125 | list.forEach((item) => { 126 | if (item) { 127 | const [from, to = "", path] = item.split("||"); 128 | const glob = path ? path : "**"; 129 | 130 | transformation = transformation.concat(` 131 | core.${method}("${from}", "${to}", paths = glob(["${glob}"])),`); 132 | } 133 | }); 134 | 135 | return transformation; 136 | } 137 | } 138 | 139 | export type CopybaraConfig = { 140 | // Common config 141 | sot: RepoConfig; 142 | destination: RepoConfig; 143 | committer: string; 144 | 145 | // Push config 146 | push: WorkflowConfig; 147 | 148 | // PR config 149 | pr: WorkflowConfig; 150 | 151 | // Advanced config 152 | customConfig: string; 153 | workflow: string; 154 | copybaraOptions: string[]; 155 | knownHosts: string; 156 | prNumber: string | number; 157 | createRepo: boolean; 158 | image: DockerConfig; 159 | }; 160 | 161 | export type RepoConfig = { 162 | repo: string; 163 | branch: string; 164 | }; 165 | 166 | export type DockerConfig = { 167 | name: string; 168 | tag: string; 169 | }; 170 | 171 | export type WorkflowConfig = { 172 | include: string[]; 173 | exclude: string[]; 174 | move: string[]; 175 | replace: string[]; 176 | }; 177 | -------------------------------------------------------------------------------- /src/copybaraAction.ts: -------------------------------------------------------------------------------- 1 | import * as artifact from "@actions/artifact"; 2 | import * as core from "@actions/core"; 3 | import path from "path"; 4 | import { context } from "@actions/github"; 5 | import { CopyBara, CopybaraConfig, RepoConfig } from "./copybara"; 6 | import { exit } from "./exit"; 7 | import { GitHub } from "./github"; 8 | import { homedir } from "os"; 9 | import { hostConfig } from "./hostConfig"; 10 | import { pathExists, readFileSync } from "fs-extra"; 11 | 12 | export class CopybaraAction { 13 | gh: GitHub | undefined; 14 | cbConfig: string | undefined; 15 | current: RepoConfig = { repo: "", branch: "" }; 16 | 17 | constructor(readonly config: CopybaraActionConfig) {} 18 | 19 | getCurrentRepo() { 20 | if (!this.current.repo) this.current.repo = `${context.repo.owner}/${context.repo.repo}`; 21 | core.debug(`Current repo is ${this.current.repo}`); 22 | return this.current.repo; 23 | } 24 | 25 | getCurrentBranch() { 26 | if (!this.current.branch) { 27 | if (context.payload.pull_request && context.payload.pull_request.base.ref) 28 | this.current.branch = context.payload.pull_request.base.ref; 29 | else if (/^refs\/heads\//.test(context.ref)) this.current.branch = context.ref.replace(/^refs\/heads\//, ""); 30 | else throw `Cannot determine head branch from ${context.payload.pull_request?.base.ref} and ${context.ref}`; 31 | } 32 | core.debug(`Current branch is ${this.current.branch}`); 33 | return this.current.branch; 34 | } 35 | 36 | getGitHubClient() { 37 | if (!this.gh) { 38 | if (!this.config.accessToken) throw "You need to specify an access_token"; 39 | else this.gh = new GitHub(this.config.accessToken); 40 | } 41 | 42 | return this.gh; 43 | } 44 | 45 | async getSotBranch() { 46 | if (!this.config.sot.branch) { 47 | if (!this.config.sot.branch && !this.config.accessToken) 48 | throw 'You need to set a value for "sot_branch" or "access_token".'; 49 | 50 | this.config.sot.branch = await this.getGitHubClient().getDefaultBranch(this.config.sot.repo); 51 | } 52 | 53 | core.debug(`SoT branch is ${this.config.sot.branch}`); 54 | return this.config.sot.branch; 55 | } 56 | 57 | async getDestinationBranch() { 58 | if (!this.config.destination.branch) this.config.destination.branch = await this.getSotBranch(); 59 | 60 | core.debug(`Destination branch is ${this.config.destination.branch}`); 61 | return this.config.destination.branch; 62 | } 63 | 64 | getPRNumber() { 65 | // This might return an empty string 66 | if (!this.config.prNumber && context.payload.pull_request && context.payload.pull_request.number) 67 | this.config.prNumber = context.payload.pull_request.number; 68 | 69 | core.debug(`Current PR number is ${this.config.prNumber}`); 70 | return this.config.prNumber; 71 | } 72 | 73 | async getWorkflow() { 74 | if (!this.config.workflow) { 75 | core.debug("Detect workflow"); 76 | if (!this.config.sot.repo || !this.config.destination.repo) 77 | exit(51, 'You need to set values for "sot_repo" & "destination_repo" or set a value for "workflow".'); 78 | 79 | if (this.getCurrentRepo() === this.config.sot.repo) { 80 | if (context.eventName != "push") exit(54, "Nothing to do in the SoT repo except for push events."); 81 | 82 | const sotBranch = await this.getSotBranch(); 83 | if (this.getCurrentBranch() != sotBranch) 84 | exit(54, `Nothing to do in the SoT repo except on the "${sotBranch}" branch.`); 85 | 86 | this.config.workflow = "push"; 87 | } else if (this.getCurrentRepo() === this.config.destination.repo) { 88 | if (!this.getPRNumber()) exit(54, "Nothing to do in the destination repo except for Pull Requests."); 89 | 90 | const destinationBranch = await this.getDestinationBranch(); 91 | if (this.getCurrentBranch() != destinationBranch) 92 | exit(54, `Nothing to do in the destination repo except for Pull Requests on '${destinationBranch}'.`); 93 | 94 | this.config.workflow = "pr"; 95 | } else 96 | exit( 97 | 51, 98 | 'The current repo is neither the SoT nor destination repo. You need to set a value for "workflow" or run this action in the SoT or destination repo.' 99 | ); 100 | } 101 | 102 | // Detect if init is needed when push is specified 103 | if ( 104 | this.config.workflow == "push" && 105 | this.getCurrentRepo() === this.config.sot.repo && 106 | this.getCurrentBranch() == (await this.getSotBranch()) 107 | ) 108 | this.config.workflow = (await this.isInitWorkflow()) ? "init" : "push"; 109 | 110 | core.debug(`Workflow is ${this.config.workflow}`); 111 | return this.config.workflow; 112 | } 113 | 114 | async isInitWorkflow() { 115 | core.debug("Detect if init workflow"); 116 | 117 | if (!this.config.accessToken) 118 | exit(51, 'You need to manually set the "workflow" value to "push" or "init" OR set a value for "access_token".'); 119 | 120 | return !(await this.getGitHubClient().branchExists( 121 | this.config.destination.repo, 122 | await this.getDestinationBranch(), 123 | this.config.createRepo 124 | )); 125 | } 126 | 127 | async getCopybaraConfig() { 128 | if (!this.cbConfig) { 129 | if (this.config.customConfig) { 130 | const configFile = path.join(process.cwd(), this.config.customConfig); 131 | if (!pathExists(configFile)) exit(51, `Cannot find the config file ${configFile}`); 132 | core.debug(`Load custom Copybara config from ${configFile}`); 133 | this.cbConfig = readFileSync(configFile, "utf8"); 134 | } else this.cbConfig = CopyBara.getConfig(await this.getWorkflow(), this.config); 135 | } 136 | 137 | return this.cbConfig; 138 | } 139 | 140 | async saveConfigFiles() { 141 | core.debug("Save config files"); 142 | await hostConfig.saveSshKey(this.config.sshKey); 143 | await hostConfig.saveAccessToken(this.config.accessToken); 144 | await hostConfig.saveCommitter(this.config.committer); 145 | await hostConfig.saveKnownHosts(this.config.knownHosts); 146 | await hostConfig.saveCopybaraConfig(await this.getCopybaraConfig()); 147 | 148 | // Upload Copybara config as an artifact 149 | if (core.isDebug()) { 150 | const artifactClient = artifact.create(); 151 | artifactClient.uploadArtifact("copy.bara.sky", [hostConfig.cbConfigPath], homedir()); 152 | } 153 | } 154 | 155 | async run() { 156 | await this.saveConfigFiles(); 157 | 158 | core.debug(`Download Copybara from ${this.config.image.name}:${this.config.image.tag}`); 159 | const cb = new CopyBara(this.config.image); 160 | await cb.download(); 161 | 162 | core.debug("Run Copybara"); 163 | return cb.run(await this.getWorkflow(), this.config.copybaraOptions, this.getPRNumber()); 164 | } 165 | } 166 | 167 | interface CopybaraActionConfig extends CopybaraConfig { 168 | sshKey: string; 169 | accessToken: string; 170 | } 171 | -------------------------------------------------------------------------------- /docs/basic-usage.md: -------------------------------------------------------------------------------- 1 | # Basic usage 2 | 3 | As per the [default flow](/README.md#default-flow), Pull Requests **must be merged on the repo that acts as the Source of Truth (SoT)**. They must never be merged on destination! 4 | 5 | Once they are merged on SoT, Copybara will automatically push the code to the destination branch and close the Pull Request that was initially created on destination (if any). 6 | 7 | ## Pre-requisite 8 | 9 | - **Source of Truth (SoT) repo** 10 | - **Destination repo** 11 | - **[SSH private key](ssh-keys.md)** with write access to destination repo (used to push code) 12 | - **[GitHub Personal access token](https://github.com/settings/tokens)** with 'repo' permissions (used for determining default branches and managing pull requests on both repos) 13 | 14 | ## Examples 15 | 16 | All examples assume you have already added the following secrets to both repos: 17 | 18 | - `SSH_KEY` 19 | - `GH_TOKEN` (note: this is different from the `GITHUB_TOKEN` variable which is available in all GitHub Actions but doesn't have cross-repo permissions) 20 | 21 | > When moving files in your destination, always make sure you have a Copybara Action config file in the destination repo or you will miss the PR workflow! 22 | 23 | ### Simple mirror 24 | 25 | Sync all the files. 26 | 27 | ```yaml 28 | on: 29 | push: 30 | pull_request_target: 31 | jobs: 32 | move-code: 33 | runs-on: ubuntu-latest 34 | steps: 35 | - uses: actions/checkout@v2 36 | with: 37 | fetch-depth: 0 38 | 39 | - uses: mailchain/copybara-action@v1.2.3 40 | with: 41 | ssh_key: ${{ secrets.SSH_KEY }} 42 | access_token: ${{ secrets.GH_TOKEN }} 43 | sot_repo: your/repo 44 | destination_repo: other/repo 45 | ``` 46 | 47 | ### Exclude files from syncing 48 | 49 | You have a few files ending in _private.md_ in SoT that you don't want to sync to destination 50 | 51 | ```yaml 52 | on: 53 | push: 54 | pull_request_target: 55 | jobs: 56 | move-code: 57 | runs-on: ubuntu-latest 58 | steps: 59 | - uses: actions/checkout@v2 60 | with: 61 | fetch-depth: 0 62 | 63 | - uses: mailchain/copybara-action@v1.2.3 64 | with: 65 | ssh_key: ${{ secrets.SSH_KEY }} 66 | access_token: ${{ secrets.GH_TOKEN }} 67 | sot_repo: your/repo 68 | destination_repo: other/repo 69 | push_exclude: "**/*private.md" 70 | ``` 71 | 72 | ### Subfolder as destination's root 73 | 74 | Sync all the files from `packages/pkg1` in SoT to be the root in destination. This is ideal for simple monorepos. 75 | We also want to make sure we can sync Pull Requests back to our SoT so we do not forget to include the Github Action. 76 | 77 | **Note:** `push_move` is commented out because it is not required. Indeed, if `push_move` is left empty, Copybara will automatically execute the reverse operation of `pr_move`. It's up to you to decide wether you want to specify it or not. Most likely you will specify it initially while you learn and then remove it to clean up. 78 | 79 | > Same applies for `push_replace` with `pr_replace`. 80 | 81 | ```yaml 82 | # .github/workflows/move-repo.yml 83 | on: 84 | push: 85 | pull_request_target: 86 | jobs: 87 | move-code: 88 | runs-on: ubuntu-latest 89 | steps: 90 | - uses: actions/checkout@v2 91 | with: 92 | fetch-depth: 0 93 | 94 | - uses: mailchain/copybara-action@v1.2.3 95 | with: 96 | ssh_key: ${{ secrets.SSH_KEY }} 97 | access_token: ${{ secrets.GH_TOKEN }} 98 | sot_repo: your/repo 99 | destination_repo: other/repo 100 | push_include: "packages/pkg1/** .github/workflows/move-repo.yml" 101 | #push_move: "packages/pkg1||" 102 | pr_move: | 103 | ||packages/pkg1 104 | packages/pkg1/.github||.github 105 | ``` 106 | 107 | ### Complex structure 108 | 109 | > You can experiment with our [playground repo template](https://github.com/mailchain/copybara-playground) to try various scenarios before you implement it in your own repo. 110 | 111 | Let's say you have a private monorepo with the following file structure: 112 | 113 | ```text 114 | .github/ 115 | workflows/ 116 | build-pkg1.yml 117 | build-pkg2.yml 118 | move-repo-pkg1.yml 119 | docs/ 120 | pkg1/ 121 | private.md 122 | README.md 123 | pkg2/ 124 | packages/ 125 | pkg1/ 126 | index.js 127 | pkg2/ 128 | package.json 129 | README.md 130 | ``` 131 | 132 | And you want to open-source **pkg1** with the following file structure: 133 | 134 | ```text 135 | .github/ 136 | workflows/ 137 | build-pkg1.yml 138 | move-repo-pkg1.yml 139 | docs/ 140 | README.md 141 | index.js 142 | package.json 143 | ``` 144 | 145 | Here are the transformations you want to apply: 146 | 147 | - Change the package name within `package.json` 148 | - Move the content of `packages/pkg1` to the root 149 | - Move the content of `docs/pkg1` to `docs` 150 | - Any file ending in `private.md` must not be included 151 | - Make sure the build files in `.github` work with the new folder structure 152 | 153 | ```yaml 154 | # .github/workflows/move-repo-pkg1.yml 155 | name: Move Pkg1 156 | 157 | on: 158 | push: 159 | pull_request_target: 160 | jobs: 161 | move-code: 162 | runs-on: ubuntu-latest 163 | steps: 164 | - uses: actions/checkout@v2 165 | with: 166 | fetch-depth: 0 167 | 168 | - uses: mailchain/copybara-action@v1.2.3 169 | with: 170 | access_token: ${{ secrets.GH_TOKEN }} 171 | ssh_key: ${{ secrets.SSH_KEY }} 172 | sot_repo: your/repo 173 | destination_repo: other/repo 174 | 175 | push_include: ".github/**/*pkg1.yml docs/pkg1/** packages/pkg1/** package.json" 176 | push_exclude: "**/*private.md" 177 | 178 | pr_replace: | 179 | npm test index.js||npm test packages/pkg1/index.js||.github/**/build-pkg1.yml 180 | pkg1-playground||copybara-playground||package.json 181 | pr_move: | 182 | ||packages/pkg1 183 | packages/pkg1/docs||docs/pkg1 184 | packages/pkg1/.github||.github 185 | packages/pkg1/package.json||package.json 186 | ``` 187 | 188 | We need the last three move operations because the first one (`||packages/pkg1`) moves everything from root under the `packages/pkg1` folder. 189 | 190 | > It is recommended that you structure your monorepo in a way that lets you run it through Copybara with as little moving and replacing as possible. It will be much easier to maintain! 191 | 192 | ## [More options](inputs.md) 193 | 194 | ## [Advanced usage](advanced-usage.md) 195 | 196 | ## Important notes 197 | 198 | This action will work on a normal usage of Git but if you start 'toying' around with the Git history of your SoT, it will most likely break the sync and you will have to debug manually. So, while you can do as many force-pushes as you want on your pull request branches, **don't do it with your base branch**! 199 | 200 | > If you want to revert something on your main branch, use `git revert` 201 | -------------------------------------------------------------------------------- /docs/inputs.md: -------------------------------------------------------------------------------- 1 | | Input | Required | Default | Description | 2 | | ------------------ | -------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | 3 | | ssh_key | yes | | SSH public key. | 4 | | access_token | | | Personal access token with `repo` permissions. Always required on *destination*. Required on *SoT* only if the variable `sot_branch` below is left empty and to decide between **push** and **init** workflows. | 5 | | sot_repo | | | Source repository (Source of Truth). | 6 | | sot_branch | | | SoT branch. Defaults to your repository's default branch. | 7 | | destination_repo | | | Destination repository. | 8 | | destination_branch | | | Destination branch. Defaults to the same as your SoT's branch name. | 9 | | push_include | | ** | Files to include when pushing from SoT => Destination (space separated globs). Defaults to all files. | 10 | | push_exclude | | | Files to exclude when pushing from SoT => Destination (space separated globs). Defaults to none. | 11 | | push_move | | | Files to move before pushing from SoT => Destination. In the format `from\|\|to\|\|match` where `match` is a glob filter to match only specific files within `from` (defaults to all). Separate each move operation by a line return. Defaults to reverse of `pr_move`. `push_move` is always run before `push_replace`. | 12 | | push_replace | | | Files to replace before pushing from SoT => Destination. In the format `search\|\|replace\|\|match` where `match` is a glob filter to search only those files (defaults to all). Separate each replace operation by a line return. Defaults to reverse of `pr_replace`. `push_replace` is always run after `push_move`. | 13 | | pr_include | | ** | Files to include when pulling from Destination => SoT (space separated globs). Defaults to all files. | 14 | | pr_exclude | | | Files to exclude when pulling from Destination => SoT (space separated globs). Defaults to none. | 15 | | pr_move | | | Files to move before pushing from Destination => SoT. In the format `from\|\|to\|\|match` where `match` is a glob filter to match only specific files within `from` (defaults to all). Separate each move operation by a line return. Defaults to none. `pr_move` is always run after `pr_replace`. | 16 | | pr_replace | | | Files to replace before pushing from Destination => SoT. In the format `search\|\|replace\|\|match` where `match` is a glob filter to search only those files (defaults to all). Separate each replace operation by a line return. Defaults to none. `pr_replace` is always run before `pr_move`. | 17 | | committer | | Github Actions | Who will commit changes. | 18 | | custom_config | | | Copybara custom configuration file to use. Using this will ignore all the pr_* and push_* inputs. | 19 | | workflow | | | Workflow to execute. Defaults to auto-detect (init / push / pr). | 20 | | copybara_options | | | Use this, if you want to manually specify some command line options (space-separated). | 21 | | ssh_known_hosts | | | SSH known hosts file contents, for authenticating with Copybara with another Git server. GitHub is always included by default. | 22 | | copybara_image | | mailchain/copybara | Copybara Docker image to run. | 23 | | copybara_image_tag | | latest | Copybara Docker image tag to use. | 24 | | pr_number | | | If you manually specified the 'pr' workflow, you will need to specify the PR number as well. | 25 | | create_repo | | yes | If the destination repo doesn't exist, it will be created (subject to enough permissions attached to the access token). | 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /dist/licenses.txt: -------------------------------------------------------------------------------- 1 | @actions/artifact 2 | MIT 3 | 4 | @actions/core 5 | MIT 6 | The MIT License (MIT) 7 | 8 | Copyright 2019 GitHub 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | @actions/exec 17 | MIT 18 | 19 | @actions/github 20 | MIT 21 | 22 | @actions/http-client 23 | MIT 24 | Actions Http Client for Node.js 25 | 26 | Copyright (c) GitHub, Inc. 27 | 28 | All rights reserved. 29 | 30 | MIT License 31 | 32 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 33 | associated documentation files (the "Software"), to deal in the Software without restriction, 34 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 35 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 36 | subject to the following conditions: 37 | 38 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 39 | 40 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 41 | LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 42 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 43 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 44 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 45 | 46 | 47 | @actions/io 48 | MIT 49 | 50 | @octokit/auth-token 51 | MIT 52 | The MIT License 53 | 54 | Copyright (c) 2019 Octokit contributors 55 | 56 | Permission is hereby granted, free of charge, to any person obtaining a copy 57 | of this software and associated documentation files (the "Software"), to deal 58 | in the Software without restriction, including without limitation the rights 59 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 60 | copies of the Software, and to permit persons to whom the Software is 61 | furnished to do so, subject to the following conditions: 62 | 63 | The above copyright notice and this permission notice shall be included in 64 | all copies or substantial portions of the Software. 65 | 66 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 67 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 68 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 69 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 70 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 71 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 72 | THE SOFTWARE. 73 | 74 | 75 | @octokit/core 76 | MIT 77 | The MIT License 78 | 79 | Copyright (c) 2019 Octokit contributors 80 | 81 | Permission is hereby granted, free of charge, to any person obtaining a copy 82 | of this software and associated documentation files (the "Software"), to deal 83 | in the Software without restriction, including without limitation the rights 84 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 85 | copies of the Software, and to permit persons to whom the Software is 86 | furnished to do so, subject to the following conditions: 87 | 88 | The above copyright notice and this permission notice shall be included in 89 | all copies or substantial portions of the Software. 90 | 91 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 92 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 93 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 94 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 95 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 96 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 97 | THE SOFTWARE. 98 | 99 | 100 | @octokit/endpoint 101 | MIT 102 | The MIT License 103 | 104 | Copyright (c) 2018 Octokit contributors 105 | 106 | Permission is hereby granted, free of charge, to any person obtaining a copy 107 | of this software and associated documentation files (the "Software"), to deal 108 | in the Software without restriction, including without limitation the rights 109 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 110 | copies of the Software, and to permit persons to whom the Software is 111 | furnished to do so, subject to the following conditions: 112 | 113 | The above copyright notice and this permission notice shall be included in 114 | all copies or substantial portions of the Software. 115 | 116 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 117 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 118 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 119 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 120 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 121 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 122 | THE SOFTWARE. 123 | 124 | 125 | @octokit/graphql 126 | MIT 127 | The MIT License 128 | 129 | Copyright (c) 2018 Octokit contributors 130 | 131 | Permission is hereby granted, free of charge, to any person obtaining a copy 132 | of this software and associated documentation files (the "Software"), to deal 133 | in the Software without restriction, including without limitation the rights 134 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 135 | copies of the Software, and to permit persons to whom the Software is 136 | furnished to do so, subject to the following conditions: 137 | 138 | The above copyright notice and this permission notice shall be included in 139 | all copies or substantial portions of the Software. 140 | 141 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 142 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 143 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 144 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 145 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 146 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 147 | THE SOFTWARE. 148 | 149 | 150 | @octokit/plugin-paginate-rest 151 | MIT 152 | MIT License Copyright (c) 2019 Octokit contributors 153 | 154 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 155 | 156 | The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. 157 | 158 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 159 | 160 | 161 | @octokit/plugin-rest-endpoint-methods 162 | MIT 163 | MIT License Copyright (c) 2019 Octokit contributors 164 | 165 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 166 | 167 | The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. 168 | 169 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 170 | 171 | 172 | @octokit/request 173 | MIT 174 | The MIT License 175 | 176 | Copyright (c) 2018 Octokit contributors 177 | 178 | Permission is hereby granted, free of charge, to any person obtaining a copy 179 | of this software and associated documentation files (the "Software"), to deal 180 | in the Software without restriction, including without limitation the rights 181 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 182 | copies of the Software, and to permit persons to whom the Software is 183 | furnished to do so, subject to the following conditions: 184 | 185 | The above copyright notice and this permission notice shall be included in 186 | all copies or substantial portions of the Software. 187 | 188 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 189 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 190 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 191 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 192 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 193 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 194 | THE SOFTWARE. 195 | 196 | 197 | @octokit/request-error 198 | MIT 199 | The MIT License 200 | 201 | Copyright (c) 2019 Octokit contributors 202 | 203 | Permission is hereby granted, free of charge, to any person obtaining a copy 204 | of this software and associated documentation files (the "Software"), to deal 205 | in the Software without restriction, including without limitation the rights 206 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 207 | copies of the Software, and to permit persons to whom the Software is 208 | furnished to do so, subject to the following conditions: 209 | 210 | The above copyright notice and this permission notice shall be included in 211 | all copies or substantial portions of the Software. 212 | 213 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 214 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 215 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 216 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 217 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 218 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 219 | THE SOFTWARE. 220 | 221 | 222 | @vercel/ncc 223 | MIT 224 | Copyright 2018 ZEIT, Inc. 225 | 226 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 227 | 228 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 229 | 230 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 231 | 232 | at-least-node 233 | ISC 234 | The ISC License 235 | Copyright (c) 2020 Ryan Zimmerman 236 | 237 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 238 | 239 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 240 | 241 | 242 | balanced-match 243 | MIT 244 | (MIT) 245 | 246 | Copyright (c) 2013 Julian Gruber <julian@juliangruber.com> 247 | 248 | Permission is hereby granted, free of charge, to any person obtaining a copy of 249 | this software and associated documentation files (the "Software"), to deal in 250 | the Software without restriction, including without limitation the rights to 251 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 252 | of the Software, and to permit persons to whom the Software is furnished to do 253 | so, subject to the following conditions: 254 | 255 | The above copyright notice and this permission notice shall be included in all 256 | copies or substantial portions of the Software. 257 | 258 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 259 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 260 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 261 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 262 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 263 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 264 | SOFTWARE. 265 | 266 | 267 | before-after-hook 268 | Apache-2.0 269 | Apache License 270 | Version 2.0, January 2004 271 | http://www.apache.org/licenses/ 272 | 273 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 274 | 275 | 1. Definitions. 276 | 277 | "License" shall mean the terms and conditions for use, reproduction, 278 | and distribution as defined by Sections 1 through 9 of this document. 279 | 280 | "Licensor" shall mean the copyright owner or entity authorized by 281 | the copyright owner that is granting the License. 282 | 283 | "Legal Entity" shall mean the union of the acting entity and all 284 | other entities that control, are controlled by, or are under common 285 | control with that entity. For the purposes of this definition, 286 | "control" means (i) the power, direct or indirect, to cause the 287 | direction or management of such entity, whether by contract or 288 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 289 | outstanding shares, or (iii) beneficial ownership of such entity. 290 | 291 | "You" (or "Your") shall mean an individual or Legal Entity 292 | exercising permissions granted by this License. 293 | 294 | "Source" form shall mean the preferred form for making modifications, 295 | including but not limited to software source code, documentation 296 | source, and configuration files. 297 | 298 | "Object" form shall mean any form resulting from mechanical 299 | transformation or translation of a Source form, including but 300 | not limited to compiled object code, generated documentation, 301 | and conversions to other media types. 302 | 303 | "Work" shall mean the work of authorship, whether in Source or 304 | Object form, made available under the License, as indicated by a 305 | copyright notice that is included in or attached to the work 306 | (an example is provided in the Appendix below). 307 | 308 | "Derivative Works" shall mean any work, whether in Source or Object 309 | form, that is based on (or derived from) the Work and for which the 310 | editorial revisions, annotations, elaborations, or other modifications 311 | represent, as a whole, an original work of authorship. For the purposes 312 | of this License, Derivative Works shall not include works that remain 313 | separable from, or merely link (or bind by name) to the interfaces of, 314 | the Work and Derivative Works thereof. 315 | 316 | "Contribution" shall mean any work of authorship, including 317 | the original version of the Work and any modifications or additions 318 | to that Work or Derivative Works thereof, that is intentionally 319 | submitted to Licensor for inclusion in the Work by the copyright owner 320 | or by an individual or Legal Entity authorized to submit on behalf of 321 | the copyright owner. For the purposes of this definition, "submitted" 322 | means any form of electronic, verbal, or written communication sent 323 | to the Licensor or its representatives, including but not limited to 324 | communication on electronic mailing lists, source code control systems, 325 | and issue tracking systems that are managed by, or on behalf of, the 326 | Licensor for the purpose of discussing and improving the Work, but 327 | excluding communication that is conspicuously marked or otherwise 328 | designated in writing by the copyright owner as "Not a Contribution." 329 | 330 | "Contributor" shall mean Licensor and any individual or Legal Entity 331 | on behalf of whom a Contribution has been received by Licensor and 332 | subsequently incorporated within the Work. 333 | 334 | 2. Grant of Copyright License. Subject to the terms and conditions of 335 | this License, each Contributor hereby grants to You a perpetual, 336 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 337 | copyright license to reproduce, prepare Derivative Works of, 338 | publicly display, publicly perform, sublicense, and distribute the 339 | Work and such Derivative Works in Source or Object form. 340 | 341 | 3. Grant of Patent License. Subject to the terms and conditions of 342 | this License, each Contributor hereby grants to You a perpetual, 343 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 344 | (except as stated in this section) patent license to make, have made, 345 | use, offer to sell, sell, import, and otherwise transfer the Work, 346 | where such license applies only to those patent claims licensable 347 | by such Contributor that are necessarily infringed by their 348 | Contribution(s) alone or by combination of their Contribution(s) 349 | with the Work to which such Contribution(s) was submitted. If You 350 | institute patent litigation against any entity (including a 351 | cross-claim or counterclaim in a lawsuit) alleging that the Work 352 | or a Contribution incorporated within the Work constitutes direct 353 | or contributory patent infringement, then any patent licenses 354 | granted to You under this License for that Work shall terminate 355 | as of the date such litigation is filed. 356 | 357 | 4. Redistribution. You may reproduce and distribute copies of the 358 | Work or Derivative Works thereof in any medium, with or without 359 | modifications, and in Source or Object form, provided that You 360 | meet the following conditions: 361 | 362 | (a) You must give any other recipients of the Work or 363 | Derivative Works a copy of this License; and 364 | 365 | (b) You must cause any modified files to carry prominent notices 366 | stating that You changed the files; and 367 | 368 | (c) You must retain, in the Source form of any Derivative Works 369 | that You distribute, all copyright, patent, trademark, and 370 | attribution notices from the Source form of the Work, 371 | excluding those notices that do not pertain to any part of 372 | the Derivative Works; and 373 | 374 | (d) If the Work includes a "NOTICE" text file as part of its 375 | distribution, then any Derivative Works that You distribute must 376 | include a readable copy of the attribution notices contained 377 | within such NOTICE file, excluding those notices that do not 378 | pertain to any part of the Derivative Works, in at least one 379 | of the following places: within a NOTICE text file distributed 380 | as part of the Derivative Works; within the Source form or 381 | documentation, if provided along with the Derivative Works; or, 382 | within a display generated by the Derivative Works, if and 383 | wherever such third-party notices normally appear. The contents 384 | of the NOTICE file are for informational purposes only and 385 | do not modify the License. You may add Your own attribution 386 | notices within Derivative Works that You distribute, alongside 387 | or as an addendum to the NOTICE text from the Work, provided 388 | that such additional attribution notices cannot be construed 389 | as modifying the License. 390 | 391 | You may add Your own copyright statement to Your modifications and 392 | may provide additional or different license terms and conditions 393 | for use, reproduction, or distribution of Your modifications, or 394 | for any such Derivative Works as a whole, provided Your use, 395 | reproduction, and distribution of the Work otherwise complies with 396 | the conditions stated in this License. 397 | 398 | 5. Submission of Contributions. Unless You explicitly state otherwise, 399 | any Contribution intentionally submitted for inclusion in the Work 400 | by You to the Licensor shall be under the terms and conditions of 401 | this License, without any additional terms or conditions. 402 | Notwithstanding the above, nothing herein shall supersede or modify 403 | the terms of any separate license agreement you may have executed 404 | with Licensor regarding such Contributions. 405 | 406 | 6. Trademarks. This License does not grant permission to use the trade 407 | names, trademarks, service marks, or product names of the Licensor, 408 | except as required for reasonable and customary use in describing the 409 | origin of the Work and reproducing the content of the NOTICE file. 410 | 411 | 7. Disclaimer of Warranty. Unless required by applicable law or 412 | agreed to in writing, Licensor provides the Work (and each 413 | Contributor provides its Contributions) on an "AS IS" BASIS, 414 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 415 | implied, including, without limitation, any warranties or conditions 416 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 417 | PARTICULAR PURPOSE. You are solely responsible for determining the 418 | appropriateness of using or redistributing the Work and assume any 419 | risks associated with Your exercise of permissions under this License. 420 | 421 | 8. Limitation of Liability. In no event and under no legal theory, 422 | whether in tort (including negligence), contract, or otherwise, 423 | unless required by applicable law (such as deliberate and grossly 424 | negligent acts) or agreed to in writing, shall any Contributor be 425 | liable to You for damages, including any direct, indirect, special, 426 | incidental, or consequential damages of any character arising as a 427 | result of this License or out of the use or inability to use the 428 | Work (including but not limited to damages for loss of goodwill, 429 | work stoppage, computer failure or malfunction, or any and all 430 | other commercial damages or losses), even if such Contributor 431 | has been advised of the possibility of such damages. 432 | 433 | 9. Accepting Warranty or Additional Liability. While redistributing 434 | the Work or Derivative Works thereof, You may choose to offer, 435 | and charge a fee for, acceptance of support, warranty, indemnity, 436 | or other liability obligations and/or rights consistent with this 437 | License. However, in accepting such obligations, You may act only 438 | on Your own behalf and on Your sole responsibility, not on behalf 439 | of any other Contributor, and only if You agree to indemnify, 440 | defend, and hold each Contributor harmless for any liability 441 | incurred by, or claims asserted against, such Contributor by reason 442 | of your accepting any such warranty or additional liability. 443 | 444 | END OF TERMS AND CONDITIONS 445 | 446 | APPENDIX: How to apply the Apache License to your work. 447 | 448 | To apply the Apache License to your work, attach the following 449 | boilerplate notice, with the fields enclosed by brackets "{}" 450 | replaced with your own identifying information. (Don't include 451 | the brackets!) The text should be enclosed in the appropriate 452 | comment syntax for the file format. We also recommend that a 453 | file or class name and description of purpose be included on the 454 | same "printed page" as the copyright notice for easier 455 | identification within third-party archives. 456 | 457 | Copyright 2018 Gregor Martynus and other contributors. 458 | 459 | Licensed under the Apache License, Version 2.0 (the "License"); 460 | you may not use this file except in compliance with the License. 461 | You may obtain a copy of the License at 462 | 463 | http://www.apache.org/licenses/LICENSE-2.0 464 | 465 | Unless required by applicable law or agreed to in writing, software 466 | distributed under the License is distributed on an "AS IS" BASIS, 467 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 468 | See the License for the specific language governing permissions and 469 | limitations under the License. 470 | 471 | 472 | brace-expansion 473 | MIT 474 | MIT License 475 | 476 | Copyright (c) 2013 Julian Gruber 477 | 478 | Permission is hereby granted, free of charge, to any person obtaining a copy 479 | of this software and associated documentation files (the "Software"), to deal 480 | in the Software without restriction, including without limitation the rights 481 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 482 | copies of the Software, and to permit persons to whom the Software is 483 | furnished to do so, subject to the following conditions: 484 | 485 | The above copyright notice and this permission notice shall be included in all 486 | copies or substantial portions of the Software. 487 | 488 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 489 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 490 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 491 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 492 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 493 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 494 | SOFTWARE. 495 | 496 | 497 | concat-map 498 | MIT 499 | This software is released under the MIT license: 500 | 501 | Permission is hereby granted, free of charge, to any person obtaining a copy of 502 | this software and associated documentation files (the "Software"), to deal in 503 | the Software without restriction, including without limitation the rights to 504 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 505 | the Software, and to permit persons to whom the Software is furnished to do so, 506 | subject to the following conditions: 507 | 508 | The above copyright notice and this permission notice shall be included in all 509 | copies or substantial portions of the Software. 510 | 511 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 512 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 513 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 514 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 515 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 516 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 517 | 518 | 519 | deprecation 520 | ISC 521 | The ISC License 522 | 523 | Copyright (c) Gregor Martynus and contributors 524 | 525 | Permission to use, copy, modify, and/or distribute this software for any 526 | purpose with or without fee is hereby granted, provided that the above 527 | copyright notice and this permission notice appear in all copies. 528 | 529 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 530 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 531 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 532 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 533 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 534 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 535 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 536 | 537 | 538 | fs-extra 539 | MIT 540 | (The MIT License) 541 | 542 | Copyright (c) 2011-2017 JP Richardson 543 | 544 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 545 | (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, 546 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 547 | furnished to do so, subject to the following conditions: 548 | 549 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 550 | 551 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 552 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 553 | OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 554 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 555 | 556 | 557 | fs.realpath 558 | ISC 559 | The ISC License 560 | 561 | Copyright (c) Isaac Z. Schlueter and Contributors 562 | 563 | Permission to use, copy, modify, and/or distribute this software for any 564 | purpose with or without fee is hereby granted, provided that the above 565 | copyright notice and this permission notice appear in all copies. 566 | 567 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 568 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 569 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 570 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 571 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 572 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 573 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 574 | 575 | ---- 576 | 577 | This library bundles a version of the `fs.realpath` and `fs.realpathSync` 578 | methods from Node.js v0.10 under the terms of the Node.js MIT license. 579 | 580 | Node's license follows, also included at the header of `old.js` which contains 581 | the licensed code: 582 | 583 | Copyright Joyent, Inc. and other Node contributors. 584 | 585 | Permission is hereby granted, free of charge, to any person obtaining a 586 | copy of this software and associated documentation files (the "Software"), 587 | to deal in the Software without restriction, including without limitation 588 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 589 | and/or sell copies of the Software, and to permit persons to whom the 590 | Software is furnished to do so, subject to the following conditions: 591 | 592 | The above copyright notice and this permission notice shall be included in 593 | all copies or substantial portions of the Software. 594 | 595 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 596 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 597 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 598 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 599 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 600 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 601 | DEALINGS IN THE SOFTWARE. 602 | 603 | 604 | glob 605 | ISC 606 | The ISC License 607 | 608 | Copyright (c) Isaac Z. Schlueter and Contributors 609 | 610 | Permission to use, copy, modify, and/or distribute this software for any 611 | purpose with or without fee is hereby granted, provided that the above 612 | copyright notice and this permission notice appear in all copies. 613 | 614 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 615 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 616 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 617 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 618 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 619 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 620 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 621 | 622 | ## Glob Logo 623 | 624 | Glob's logo created by Tanya Brassie , licensed 625 | under a Creative Commons Attribution-ShareAlike 4.0 International License 626 | https://creativecommons.org/licenses/by-sa/4.0/ 627 | 628 | 629 | graceful-fs 630 | ISC 631 | The ISC License 632 | 633 | Copyright (c) Isaac Z. Schlueter, Ben Noordhuis, and Contributors 634 | 635 | Permission to use, copy, modify, and/or distribute this software for any 636 | purpose with or without fee is hereby granted, provided that the above 637 | copyright notice and this permission notice appear in all copies. 638 | 639 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 640 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 641 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 642 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 643 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 644 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 645 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 646 | 647 | 648 | inflight 649 | ISC 650 | The ISC License 651 | 652 | Copyright (c) Isaac Z. Schlueter 653 | 654 | Permission to use, copy, modify, and/or distribute this software for any 655 | purpose with or without fee is hereby granted, provided that the above 656 | copyright notice and this permission notice appear in all copies. 657 | 658 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 659 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 660 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 661 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 662 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 663 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 664 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 665 | 666 | 667 | inherits 668 | ISC 669 | The ISC License 670 | 671 | Copyright (c) Isaac Z. Schlueter 672 | 673 | Permission to use, copy, modify, and/or distribute this software for any 674 | purpose with or without fee is hereby granted, provided that the above 675 | copyright notice and this permission notice appear in all copies. 676 | 677 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 678 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 679 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 680 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 681 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 682 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 683 | PERFORMANCE OF THIS SOFTWARE. 684 | 685 | 686 | 687 | is-plain-object 688 | MIT 689 | The MIT License (MIT) 690 | 691 | Copyright (c) 2014-2017, Jon Schlinkert. 692 | 693 | Permission is hereby granted, free of charge, to any person obtaining a copy 694 | of this software and associated documentation files (the "Software"), to deal 695 | in the Software without restriction, including without limitation the rights 696 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 697 | copies of the Software, and to permit persons to whom the Software is 698 | furnished to do so, subject to the following conditions: 699 | 700 | The above copyright notice and this permission notice shall be included in 701 | all copies or substantial portions of the Software. 702 | 703 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 704 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 705 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 706 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 707 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 708 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 709 | THE SOFTWARE. 710 | 711 | 712 | jsonfile 713 | MIT 714 | (The MIT License) 715 | 716 | Copyright (c) 2012-2015, JP Richardson 717 | 718 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 719 | (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, 720 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 721 | furnished to do so, subject to the following conditions: 722 | 723 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 724 | 725 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 726 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 727 | OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 728 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 729 | 730 | 731 | minimatch 732 | ISC 733 | The ISC License 734 | 735 | Copyright (c) Isaac Z. Schlueter and Contributors 736 | 737 | Permission to use, copy, modify, and/or distribute this software for any 738 | purpose with or without fee is hereby granted, provided that the above 739 | copyright notice and this permission notice appear in all copies. 740 | 741 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 742 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 743 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 744 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 745 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 746 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 747 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 748 | 749 | 750 | node-fetch 751 | MIT 752 | The MIT License (MIT) 753 | 754 | Copyright (c) 2016 David Frank 755 | 756 | Permission is hereby granted, free of charge, to any person obtaining a copy 757 | of this software and associated documentation files (the "Software"), to deal 758 | in the Software without restriction, including without limitation the rights 759 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 760 | copies of the Software, and to permit persons to whom the Software is 761 | furnished to do so, subject to the following conditions: 762 | 763 | The above copyright notice and this permission notice shall be included in all 764 | copies or substantial portions of the Software. 765 | 766 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 767 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 768 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 769 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 770 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 771 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 772 | SOFTWARE. 773 | 774 | 775 | 776 | once 777 | ISC 778 | The ISC License 779 | 780 | Copyright (c) Isaac Z. Schlueter and Contributors 781 | 782 | Permission to use, copy, modify, and/or distribute this software for any 783 | purpose with or without fee is hereby granted, provided that the above 784 | copyright notice and this permission notice appear in all copies. 785 | 786 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 787 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 788 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 789 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 790 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 791 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 792 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 793 | 794 | 795 | path-is-absolute 796 | MIT 797 | The MIT License (MIT) 798 | 799 | Copyright (c) Sindre Sorhus (sindresorhus.com) 800 | 801 | Permission is hereby granted, free of charge, to any person obtaining a copy 802 | of this software and associated documentation files (the "Software"), to deal 803 | in the Software without restriction, including without limitation the rights 804 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 805 | copies of the Software, and to permit persons to whom the Software is 806 | furnished to do so, subject to the following conditions: 807 | 808 | The above copyright notice and this permission notice shall be included in 809 | all copies or substantial portions of the Software. 810 | 811 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 812 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 813 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 814 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 815 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 816 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 817 | THE SOFTWARE. 818 | 819 | 820 | rimraf 821 | ISC 822 | The ISC License 823 | 824 | Copyright (c) Isaac Z. Schlueter and Contributors 825 | 826 | Permission to use, copy, modify, and/or distribute this software for any 827 | purpose with or without fee is hereby granted, provided that the above 828 | copyright notice and this permission notice appear in all copies. 829 | 830 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 831 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 832 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 833 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 834 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 835 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 836 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 837 | 838 | 839 | tmp 840 | MIT 841 | The MIT License (MIT) 842 | 843 | Copyright (c) 2014 KARASZI István 844 | 845 | Permission is hereby granted, free of charge, to any person obtaining a copy 846 | of this software and associated documentation files (the "Software"), to deal 847 | in the Software without restriction, including without limitation the rights 848 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 849 | copies of the Software, and to permit persons to whom the Software is 850 | furnished to do so, subject to the following conditions: 851 | 852 | The above copyright notice and this permission notice shall be included in all 853 | copies or substantial portions of the Software. 854 | 855 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 856 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 857 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 858 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 859 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 860 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 861 | SOFTWARE. 862 | 863 | 864 | tmp-promise 865 | MIT 866 | 867 | tunnel 868 | MIT 869 | The MIT License (MIT) 870 | 871 | Copyright (c) 2012 Koichi Kobayashi 872 | 873 | Permission is hereby granted, free of charge, to any person obtaining a copy 874 | of this software and associated documentation files (the "Software"), to deal 875 | in the Software without restriction, including without limitation the rights 876 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 877 | copies of the Software, and to permit persons to whom the Software is 878 | furnished to do so, subject to the following conditions: 879 | 880 | The above copyright notice and this permission notice shall be included in 881 | all copies or substantial portions of the Software. 882 | 883 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 884 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 885 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 886 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 887 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 888 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 889 | THE SOFTWARE. 890 | 891 | 892 | universal-user-agent 893 | ISC 894 | # [ISC License](https://spdx.org/licenses/ISC) 895 | 896 | Copyright (c) 2018, Gregor Martynus (https://github.com/gr2m) 897 | 898 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 899 | 900 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 901 | 902 | 903 | universalify 904 | MIT 905 | (The MIT License) 906 | 907 | Copyright (c) 2017, Ryan Zimmerman 908 | 909 | Permission is hereby granted, free of charge, to any person obtaining a copy of 910 | this software and associated documentation files (the 'Software'), to deal in 911 | the Software without restriction, including without limitation the rights to 912 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 913 | the Software, and to permit persons to whom the Software is furnished to do so, 914 | subject to the following conditions: 915 | 916 | The above copyright notice and this permission notice shall be included in all 917 | copies or substantial portions of the Software. 918 | 919 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 920 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 921 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 922 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 923 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 924 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 925 | 926 | 927 | wrappy 928 | ISC 929 | The ISC License 930 | 931 | Copyright (c) Isaac Z. Schlueter and Contributors 932 | 933 | Permission to use, copy, modify, and/or distribute this software for any 934 | purpose with or without fee is hereby granted, provided that the above 935 | copyright notice and this permission notice appear in all copies. 936 | 937 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 938 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 939 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 940 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 941 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 942 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 943 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 944 | -------------------------------------------------------------------------------- /docs/images/gitflow.png: -------------------------------------------------------------------------------- 1 | �PNG 2 |  3 | IHDR�UUo:� 4 | �iCCPICC ProfileH���TS�����!��z�H�z� *! $���]GpT�2��" 5 | ���b��(�Ƞ���*��0��z���u��������u�]����8U�eK"}�q� L�.��Xs�R1+<<0��ׇN��-�X���� 6 | �/� �'��L��bc�+�d�vcv�������+�#�:�#�<�x��OT�/ƚd�#I�cvf7�C���V��0�~�'W��a��)��Y#܍�y�_��-f�<&��*籽���'��38�������̐��0�M ��f�.�nzV��E�3��Y��e�,(z��R߄q�q�B�k3f��s�0�-��͎g��?r�%Y�\)_�8s$ye��r��ϖ��DŎs�0f�8K�#C&||�v�,B^?_�3�7@��L�_�+d��f ���{�L���&bJ����~�>�rq��<�8#\����ۥ9���؁�X.�i���q�P&DcsD b� ~���_8rF�7K�+� 7 | ��,��l�f 8 | ����`�Ύ�w�w�85a�����OؒK ���'l�;��Z�2IΘm�:���� �@���������! �7�� X�a@l��P;���A8 �p��E� 9 | 7�<�n胗0`ABG���� V�=�x"�H(��#IH*"Bd�bdR�� �.��9��E.#�=��G�"_PJC�P]�����,4�B砩�|4�Gף�hzm@ϢW�;h7��N��3�Y�\p��0\.'�-���pU�:\3�w ׍{���'�x&���G������u� 10 | �>|�<��?��N�tV7�GH%, �{�w}�D"Q�hFt&�i�E�u���z�b��8H"��HV$R�C�&����N�n��H�� 11 | d}�=9��@�W����ɧ�7���Ce� ōF�Qr)(�)͔�>�U�jF��FQӨ+���:��C�;CW�Y 12 | B�� 13 | � 14 | �.)�(|���,i��D������v�v���N��ҽ� �l�zz ��1��"C�F���S\�X�ؠxS�E�D��4W)O�L��u�W�eSe_e��R�J���]ʃ* ;�0�L�u*�U.�߼������"�b�� K���R`Yiy� 15 | �r�Zm��B��:E4�jJ�5͚e�c]k�c�aj�Ҧ���T� S���N�n�h�a������]��J�f�����\�J��t��eMo�YM�O�1��#�q����oN�N�:�~gc�$�m�].j.�.�\.�\}\���p�����v��Owk�t���ϧ�M�O�=���Ѓ�ˣۓ����g���ǫ�뉷�7�{��3�+�u�����G�s�磯���3~8�@�B�vU�h� 16 | �����������B����غl.��=��$�|-$2�"�I�e�$�y:#xƦg���l �0vئ�G�f����E�>�r���������y��#?D�Dm�zm-�n�Q�I�����[�75nI��x�xa|S)!&aO��l�ٛg�%:&$v�1��p���s3果�4�3�H!)6i�WN��3��Nޖ<���n��y�Jy�|~ �Y�GJI��T��M��/A����WX!|���3�czX����،�LrfR�q��(]t>K/kaV��J\ ��6��I�d��Α6e�a�Q��\�Z֓�S��iÂ# U���Z��}����"�"��W,�Y�Z�k)�4yi�2�e����.߷��"}ŵ��+KV�_��9_7y~���յ����5�kv���A�C�Z��[�~/�^)�-*+�����ʏv?��8�>e}�� ;67�6v{�+Q)�+��4cSC)������y�/�M+۹��E���<��i��֍[�V*�T�T�o�ٶv�����7wx�۩��h痟�?�����ʴ���X�S�tw��֟]~�٣��hϷ�����"���q��ٯ�C-Z+��?�x��A��Mu�u��5��!١�$��y8�p��#uGM�n;�8V؀4�6 4 17 | ���:�oivo>��ͯ{O��<�~r�)��Sç�N��yu6�lo˼�����>?�|��� �.\<��j=}��҉�n��_q��x��jC�c۱k�׎�;�7\w��t��Fs��S7�n���w��m���wf����ە��}�w����{o���z��!�a�#�Ge�uW�f�[}�S�����'�O�r{_�.��k_�S�Ӳg��j��??�����}/�/�^���Ƕ�每���g�@�@�ɛ���i���~�������2? },���i�g�ϭ_b�<Z�����ŷ��!�g�9�h+�����v/=�q�:{��2�0J�x���@u@�"��k[+���������up��J��`?�慵&���ߙ���U�Ɗ}p&w����]�������m�/��?���VeXIfMM*�i��D���UASCIIScreenshot�AN��iTXtXML:com.adobe.xmp 18 | 19 | 21 | 899 22 | Screenshot 23 | 341 24 | 25 | 26 | 27 | �e�@IDATx�]��үs������-8 �%J�^�����_ܿ�����(1b�$�@�����}��ݞ�����ӽ�����tWWw�{�gj��*dۖ�E$$�� �� �� P�GkCCC($$�V5\+�� �� �@mD���� 28 | �H)� ���F�͂� �� �� �* 29 | )%��X��]+�� �� ��Q�JA@A@A�" �`-�ti� �� �� � �5 �� �� �@-D@��Z���dA@A@A@D�k@A@A@�Z��(���ӥɂ� �� �� �2(׀ �� �� �Qka�K�A@A@A@eP�A@A@A@j!� ��N�& �� �� �� ʠ\�� �� �� �BD���.MA@A@A@�A�A@A@A@���2X ;]�,�� �� ��(�r �� �� �� P e�v�4YA@A@Q�A@A@A�" �`-�ti� �� �� � �5 �� �� �@-D@��Z���dA@A@A@D�k@A@A@�Z��(���ӥɂ� �� �� �2(׀ �� �� �Qka�K�A@A@A@eP�A@A@A@j!� ��N�& �� �� �� ʠ\�� �� �� �BD���.MA@A@A@�A�A@A@A@���2X ;]�,�� �� ��(�r �� �� �� P e�v�4YA@A@Q�A@A@A�" �`-�ti� �� �� � �5 �� �� �@-D@��Z���dA@A@A@�A@ʂ����(�x����EL��c�����7�������lJ;z\��%�SL|�K���‚:�ke�eP�V�T�~� ۡ��)'3���oCa�aV�.�n�zeŧK��,�ߴ����/?/��:J Shh(K>J��y�\P�Ӕr�09�L���R��&%��|r��|�Rhx(�k�P�g&.�i���K�& �輜\:�s/���=V�A��&O��*�Ǹ7AEEEtd�!2ǭ��BJ��%�� ��1y4e���!��y�kԼ)�g�:�������cXs�1 �;z�05hƲ���(�붔6����� qQ<է���Ŕ�#�4H���hc���ؠYc�D� 0F�A��$ͯ�n1s ,"\a��K=��q�a�&�>�G��l�xiʑ� T,� V,�"]��|���q�za����� ���������R�>�����^��4��Rm�t�s�m�F-V{�O����b��#/���s��v=;ӽ�=g�K@\x�ʻ)-��V�E�}��e�?ђ���G?]eܲr=�}ϳVD���#�턾*�����c���Ӑ.�f�{�p 30 | =x� t���m�NJ z��i��*?�<�ι�ru>뫟髗ߧ��|u�J�k�?s���?+���� Ϡ �u���]�^x+M��sљ*~�kPVz��Qڸj ��t\L\,]�����!*ʔ�y���?f(-���>~�-z��).��J~����Y8�������s�I�=�oů����خ��C���c�F�~�i5���i�����Sb�V���S���n2�KX* Q+ h)F���k��ek���\������9ƥ��F ���m�M=�:M~���',�x~�x�_�%��_��^�� z��):6��сY錈�����:��A�7m�f����'���߾M���#�Ї��FO��g~ ���ot�5����)EP�7����<�������^�VD/��z�!p �[WmP����]EC�8I�"�I�Y�(��5蔑Jq�����C���^���w>���u��7�٪z����&o���t��ͯ������ 31 | �;vSx�c�����N�~' �so�� 32 | � i�ƭ����j�셪��s�(��������a<#ڀ���t*Qi�j1'��c,f�z�}���TK_x�.V°J�2�K�[���<�r�shˊuJ!W ~��a���AP�6-Ԍ����}﮴c���jH�G�aƪ8���G�x�C�-% 33 | �@�#�z��Jqr�����E%ZE �P�A�i���ƿW�&i 34 | �C, �ӳo�L-KZ3o�N��X�����1��/]C�ld�eZ�����P���rӡg���<[�g 35 | !��({��eJ���f�%�g�W>3���x��Cc�8^��Kc[� �2����� 36 | S��A�,nj����R��h񌹮 ������X"�%�݇���3�6~b��a'*&����Dqu�}��j& �EE�I�۵�MS�M���y��b�"5�zAiuG�ia��N���&�c��o�VJ���tj���Tc�/�c��%�P��x�a�*^~�O�G �Y�1�iY� 37 | �%��g��]t���fgd����k70�奧�:�u+O"A��(9�T|�R� ��R�eX�� � _�=^�@�q˂�(S)nұ'ԮG�y��'-%�gZZi:�xMP���xA D=M��F�s�N����YJ $u�@X���Г��I_����kSJ6I�z��mG�w���t��+荻��%3�ZJ�����%�%��Ā�>�J�<|�'����L�{F�;�0���9���~���Z�Q�ޭ;)y�y�x� ����U�X����wBKW�o{�_���4���Bh*�k,��/�]�?��I}F �� ������8�Ш��R"���O[��|������(f1c�mp��q���Q~A��e��1��� ��S��Խ�Z���_<�*��}�F<�E�P��I��ٌ��/K����Z��ޝ���EVE�E4�_�V��7�6e����N����y;�ҩ�'=Ey�ۆū����.i�� `C��Ǐ��0[l�)V`Y�E�\O�x5�{�y��<[1x?�%xau�3߿�����Յ~��[޻����Y��5w�9��ƴ���~��Y F��7iD < ש_w�+*�� �N?�=�0��JWa�jݢ�l�|����}��KB!�I�D=�4������g�`�q,�_�+*��FX֫�%u�h�cE�~^&��@(�Pb��\���l�vk��Y�� P��|���D� �`3l0s��שf-w�q���l4��i����KBXⳓ�^g7Ң�`%{z4ao 38 | ��� %{ ���|������(�G_��<���%�qN��:�˕�DX=����7��Io�=v��C$�;�#�q����<�ӳ,�X��K�����(�1;0kg�a���?�o�ޖ�y1��;N��fO�E��UN���a�]� �,�u��T"\ڸ�{���t�Sw�e��7�p�ѸE��7�>��q��f�&!A@��@��@�Ml�V6=�@6g��/��'�=��S쥍O�M�B+�.�F�b�'�9��R��UdX<���}�H 40 | �7���<>uS�#���t�:q��]��^�Կ����j�}g\�f�^rV1++�p_q��+�4� �Pi㪝簊�Y�_؀���YTԌ �h롈����K�a�4B�`X ���PA�A���i��b��������@�! �`�A- 5|�����NV>��2X��B�e�:�ܿ'[� Qz,��l �=�_�=�}��{�nx�^���&bW=r�ڏ����=,0.q��2x�M��K�!z��ǩ��#���6��BE��Q� 41 | ��]�7���W�g?M�/���\}G<�+\���,�I‚���f~����4���B��+�l�>*��0�Eb�1�������1��1���I�<��|���g��ʁ#���OU. �%+8��{���� �E�ؚ)�*c��ԫϷ��X��J3!3���Cԅ ����� ���]��j,�r|W5/��ñ�N�I�>�c�z���a�e�ge��f���g� J�3��z�٬�{�gR��<��k��5� 42 | ª��� 43 | �`�NI:�A� 44 | ٶe}Q���,�|UP)R���T8��y�Hg�>F��Ł��SY���,���[WV�R� P[���4���-s���~o�Ǝ���ʑ�}l� (2p*����#o�rk�.�Y�` cygl�8�薥�������!���$e�/yA�?0f��#Q��M�A@A@A@j�2(d���� �� �� ��(���&�A@A@A@jD ��� �� �� �� bM40�j]��@�o�:�/�2�Svf6���o�ȏ�����j�؈[5�Fl۴`V���W �~��]ԺK�jQ�D�"�Yl\$���g$�<��x +��$���D����7��l�6�]�8V�}&�W ���f� ��!��A����q��X 45 | N����T��ME�9Dl��"")$*�B�c(�aC 46 | kԘB�&T��KM�-� Vۮ�ڊ�fޫ�����7І՛(��7�JP;vkO��v���R�.���W����شt5}������L\���C�l�C��������f}�p��3�&];P���~���f>A ����w�g�0u���nGM�<���=�(o���L��w:?M�Im)�} 47 | �Љ›���Q���Ěh-�d_�x��q����;c�޾��l��5IlL����RS�3$$Tp�m�F�6�Oe'eT2y�o�_Kh����Ƴ��Eux�0����?�g ������U�q�D�����������Y�7�XJ�<X^ִE�e?�}�Sh|��+r�mMT�� ���v������Ь_沏����"+$4�N5�ξ�tj�N��A@(/�����wڱ`)�#�����PJҟ��1�� 48 | �� P^��Oٳ���+��2��ؒr��(j�`�1���7(�.1�QkE7{odϜL��{����Def 49 | /�a"%4��앉��%�4r�i��?��?U�˓��j?j0�<�t�����ʹ >#P��F����f}�T���u�p�;��F��D�D�2D�UU]K-��JH��@9"0��_�m��Ԫs�r�*�e��3�d{b����:u)�̳)�{� 50 | /K 51 | �>he0���ny4&&Z |T���К��<(����egU���FbI�E�hǖ��k`e��B/�k[V��i�|F'_2�V��64V@�����~��Z�m�5ҽ��P:/�oڣ3���]���)��|����k�v�Tva"�#�Y����'�3oQA�m��X3�-�bi*f(#�w�^F*T����l�H+�`��k��G�S詻��� WYq�!��]V,�c1��ߍ�׭U�:����S�ĦԲcR h�4A#�~�0�z�-:�a�����{�Ӟ�WQb�.'�F�E�H%B�N��ԡwW��1T�b����w��,�VL �I-ػ��6������E�, ��ɥ�A1 <}V���ݵ����E:r���R��Bn2��Ȼ��������n��(A��#W��.夦W[,��zԿ�Wն��b�@�"��c;����X�5�\zhB=�s���X,�{F)�S�2���o���4vm�C���t�VцL�e��=/*����I�A�v!p�����3���I,���z��B�� ��m�Di�'UkE�-<~�R'�A���՗p E@��ڱ�Y�ӳ��D�Ug`A�ŗc�y��7h����� � �������) 52 | ~R���/�K��W���� }�L��/E=Q_�[H6��1���lծ���߳��>�� &T��@�*�=�i�K����Ǣ���e�����2���3�4�*-���~�z��W����sH=*3��~Nw�x e�/۸t5�<�|z�;]��۔itӐ����t���T��o���?|�Mt�� h��MV���)t��莓.%8g״n� 53 | U�W�[G����V��<7�%�����>��M��%���o�[FL�]ƾ.X��m�Et���(/'��_���t��������C��}�����\�ߺ�i���V<ֽ�wƵt+��w��g���/�֑ҿ�]�����g-Te�|�Ö ��S%���?t������3��� 54 | �S�Veع׊߿}�*ӎ�� ��:�J�_x�ry�@E���_B��Z^���ި�P����{���;����!�GM�O1��5 �5�N��&a��� �h�7Pf��V<��3o0�h�x��L���x�g&a�C��4a\���q�&��O1��L0�b6 �4�ĸm�u��%mݰ�C��>圱t�-���`�Xn@�2_��M����agk��Q*��1������w��Y��״}��rZ��.�>XT�2�B��=�z�}J��%B(+2����ϟ��w���/� 56 | 57 | k�A/�ҭ���(㻯���feR��(z��,Rʪ`�2(�*+����#A���[��/pQ!�K�N��+�рa}P�_���6��P�`����_Բ�`�3�/���+��/LuYr�*g� pl�>Z��o��R�+�u�"s�DdE�y��.�슠J��]��Y �L�K�O��r>�G(p�����+1��e�x��t��P�Є�s�nA�է�_xY�C���O��B5Qk^�*��y�5<:c�xK����s��/_Ϥ�;/2X�w���Q���)ǟ<�_�� 58 | �� �� �T^b��t, ���O�s��{o�$M(0���yX&Z����G����y�ޒ�_�W��K���m��@�C��w���R?�4Ԙ�e4c'�'���/���o�>�V9fo�(�oE��^���D����?~����N��I-Ԟ�\�G�nņ���7S�4���Np���7k��w��C9� � 60 | ;+��1#�9�e�M���"�ۃ����\s�׊�̔�s�2��O���\���q8��;<�j*�hL֜Y~7/�A���۰���Lyl@F[��*��Y�t�S�<)*}UJMj�(�5�7�,[W&�.q�ձ�%ׅc�V�@��'�K�Y�F7?p=�xߵt�y�X٬��k�Q^v7g�<�<���a�*ڴlm�5V6粑!W��_F�j�^W�V���6A��ˏ}���脒�Kt�c����L��*nY�e���}¸�D�]h����~�}%��_���Fx��WS)�g�R�Wҋ 61 | �ݕPd��l '�0��E(�UA{vQ���UQt%�YD����z��Q��2^ ��"���h)���Ѳ�k�9���;U-�q]{wV&�qn��t�K�^s�ZR��G�<�.��l��3�sg���>�w��G��L���~5�6S�u�ҭ'�y;�*^�5��?�������j��M3|[�}�� $9�����t����ح���\/c��\� �-����݋W��{�I ��]�hY���v"m�5��������C�Z �m�K�7�^r���P�r�|.�����`i�Y7\�+������3g��.��H2ffPhlEv�I�M�Q��b?��#FS��� ��Wes�"�w���+�\(E9�J~��<@aM�����B��`�3X :�<��ʛ���xg 2�2��k��ԸY#eI�I�ƴn�F5إg'����)�������eT�^��s���7]�>�JN<�����h�o�Fi��=���W�!}�����o���(�֕�i�e��*,���ٍw_�Q��b��_�7�=�v(0��ff�U�«���˜�X���qf��)��� 62 | �D��3+������?HI#Q�}Ò�VzQ4��kѯ ���Ww�OL�E,���P�����z���2��?���|M�/5,�kX�D}p�c�Q��/i�2���`�_խ�6�8��艹ʈ |�Eq�f�?T����o�;���mP�������-+�<�+Ę �D�YS(�� f��=�氆Emٷ ��r(�xZ�>E�8�b��Tr16e�{�"�WXT�VE��֜~�E�G���q��22(�����Y�H����ҳ f-.��S؏�#�ܯk;���8�rrr��y �:~(/��~S4��bF��엯g�o�f�Y�3/<�F�2L�]u˥�l�Jk٩���g�o�m�6>pVo����g��?��ya����0�;��N']x�9�Z5�ݧ�y��s�P��Y8V<~���,�v~8�V �����Ң_�T�e���h���E�H�A�&8 63 | yF`'+l����CŎ�#��U��M�_?S�:,��[+*�?|���}�0�`2���G����F�����F�|6rcόv����g�/^I�6�p���u�v=;+�U�Oe���,�f�dgd)L��I��v�Ǹ�#����WN���Uy�r�b��i3/�[LJ�}��(����Z��,��sV�?��g����;t���I�l>�bǖ�Q���t*`K�UF���]���� ��*T^�ETp� �4mF!���Wl%�$3��vE���o��J)G�Ѯm��Ϡ^J!����Ȇ�W{�r�s�5����_����,���(=-�0�����Ա55o�H��U����9�V��H�[�ɞ��}��}��Wt�e��l'āo /'���*�H�KxUf�y�#z���i��m�+���@Aܸt �c�j�Z6�Ķ�/�s'�����{�[Q�`���e�f=�,�+�Ÿg���'��HVZ�J"���i����,�]�^��#�0����?�y� 64 | x��$� �J�!�h�bfҮ������۾��e���9�L�>����s��C|�M�p90��iڤ)����p���s�����C�N}�Y^wy�9n٬�ꭏ��y����avp���������.zV�L!���=I�~�z�yz��y���yʐZ����la��W��*�ן�rW.��f�n} €L�'�_^�:#���W-��j�̠�X!��FEY�tu|0q?�5�`�A���j�i�#�lA�����t�B^y�MJ��? ��&w��8�W�)�����6���5z�ʻi��M>Wg��u��Uw+��L�̘�/�P`g|��g1=��?��}�g��������箹��n��s[����� /�rE �g=Rʀ ����d�tG��"�����y�����{o�!�]�£_2yh��+�/p�>���^܇�k*a�x�q��8���6��"|8D�9�+��$��H�RK(�Ll&o����И���ê��];����TM�ٸ �!��FeJn����2 �e�6ʵ}�.�8�Y],]��ċ���ؒ{:̸��H���ce�7,���c���=E�n�V�&'�gk߸� :���*���h򣯨�Z����[ӂ�$k�γ��.�}ﳄe[�ґ}��u�OqJ�\�X[�Y g�(���A`�b���T� X@�ˈ��� �!�Gܗ5�0~b ���㷻ٸ@����+<���������`�| 65 | \y�#j�c�`y��K^^�e��E5S!�6{�Kq�C622��������5�e������vȉ�}?\' �]�=N9YYf~�J�W3�yt��h�����婨�Mk���N��\v&�_����_ұoPx���VQ�M��Z�~ 42�CՙF��d�O�z���)P:ή5���3t�*k;�ɿ��&-c'�e���~����Q�� =� �V9�����,���Ŝ�!�T*�D�]w��h��4��1f҃/�2,��1���1y�z�e�w���dS���f�<7��9tQ��X�j��~g��d���Q���<2zIؽ��\�h��+[ �,��{���i� 𷺏ú?���̤3h!�܋X��vM��)o|L�/%܏o��4��̿{!T_��]N6�����LY��}��(/[��Y��=cҧ4d�в���:��=�����O���y�?�n{�6WV v)�l��˧_ ӔoV%oC�����ԅ�['f窒rV���v75�C���= �~��ޟ��ss����� �E�����åu�5�ˡ�נ=��BIJ?�ڜ<���Q�։>+�G�Sx��a�S<;���,�We���������b� 66 | 3h�w�Q��P��S�5�.}���g��� L��"[�3��A�pr9,IP����t� 67 | �w�Ibc�c�'����y��Փ'�H�Z4U�l^�5`�������|l�xm���\��U��h�i%�������{����N���m�zjף�����`�Y�P\}�����Πf��~7�:4P�f�g=�&�p�G�c@�O��5��5�>���"�B�<�&�:� 2Řa-��n�"Z�{����Z^�v�i^ھJݕ���oq��E#t�>"��G%VG9<%��ͪ�X�����I�d�gM)N���Ulԩ�(�H 68 | ��F��R5d&|v)�s��M&3 f۹.S�� �[,�s�/��3h&>�Ɔ}�� yY��^�k���O(� �AWG�w�IW qfX��]��x��p�pz�D>�V��s���q�W?�; ���k�_�J���s�W٬����s3N1x����_7�)����xՒU�s`��t��G{�5�f����)^��;Z}˙Ͱ��L�E��� uW�/qq�N�㯿H���� 69 | �)����1�'�"h^?�Ee��|*��t��;�h�'�T�ߥ7���dcs�7{�U�f)�U� :�yX:� ���q�vJb˺�F��e��QfF�%J�7:r�(�'�(�_vl�M�ؚ%,z#�N����f�.Bu�9Ϭ��#񲩙�U�ύ���aԹ�fM^{�����(6��ԡ5]}�e.�K;�No>��K�?'���Fä���ѼO�24�:����v�q�7�z���p�<�2Ȍ�x]���������x�U��ܼ/uy`�� ~X�`��*�y��4�̓� �R�����!=�.�����9���B5&:�t�]�j/Gg��3��g�����t�����y�z����tQm����:ے�t�����YA� �GN�rTYָ ŝw!e|9���,Q���p���;�yA�>��� ? 71 | +ȏ,� ^Y!<�����O�[���x����V�1���_�ų�f��Ŭ����"y������ 72 | ��(�[G��G)���|�7p���i����7?�v����"�$_ɨ��Y|�r 73 | Ч�*n9�|#}#w��X�Ƥ��t�}����W_�a.���������d��~° ������*\�^3zO�_=޹KO���ȩ;� c��M���4�����TW�uX� 74 | �a��GV�l�2ր���>-�oڈ�,�X h?ߟ�xY}�F���8]�{���"�^�L�,k�����'�6�4mlFY�A����AD��*�A��>1�l���q&��m*��K���)��;BbK�Q�����G�2�N�޺��]�q����A��Ѻ�t��U��XT�~�$=�`���zMT���C{˭/�>�p7�{ 75 | ۚl�VlɁ�����V�2����S;��U�H��A�y�x�����Uė8��{��y�O��2��l���0�:Žka'���8G�)Ӗ��ُ�e<�^���=;��q�ɮ ��|YA���M!Ϣ��� ������xJ�_5n!ׅ@�f���I��iΖ���[wi���?Ϣ)�MR�\.�f���Ki����{��Ü,��4X�M޳���k�,�^]Y������W;� 76 | �,Ƽ���o{��p,�� 77 | ܍\��������ޯ�9�_<�~�:󺋕Ix�����þ*���*c ?��>�QJWy,a\�\Ih��o���:��R�K���?T�S�y�.��_��^�d�� S�Mi��G�\�E�@�c �!Ҍ(lV�`VR��4΀��� 78 | x�,���G��ЖE����lK&"5鰝QWČw��,�†F��5i����y@u�]�M�W� ^{��R� �*�L]-}�*ӇD��|.@U�)X�w6�I%Yv٬c�5��8�����w�� i���][��s{if==��yJ��8μ�gj�~!��5���uPY�o�SJ��eq���ܻ}/u���o����T�ˌ�?��W�p3�%�r4P�ȉJyw>�4oَ��(z� 79 | k�H_|���dH��>�"{���:u�����)��?���a������km@cg���X���tF��8��8gu�0�r��%��Pv��T�� f�|!��Mff>_�!�I�R<�̺��\�K��43N�BN�9l�W��Y��@�����U��v�D���.a����s*ɒ�Qt�A`ܐ��)5k�v8��),CiL�T��?��<��`5d�~�R�i1�I����Է\���g 80 | �����g<���0����s���Xʄ{��fetX�D7@���tu����6������ɳ�����V 81 | ‹��ag�i�%g�ā�{�g9�I=(���J�����3� ���w}/�ط�щ�}�9�� �����t߾x�=z��7�q[��<�|� �xTu��}�_LpDTYH��E�U���������.� ; v��,u��yQ~p6��a{|Yگe��J��N���yp����f�Ѽ�p���wP�sKf����Cx��t�u���?�����Rw�o'����_;.�<��s,)[x��J;�ـ�� 82 | �tG3�,��L�rb�f��be��ӭ������Ҩ���}j���תڮ�W��/髶t�m�(��{){�<�Y8� 83 | S�o 84 | oӖ�/��Bc��l@9��?�"{�����P��ե��(r� �З� �ς5��G�25����X.�q#WE��T^"��G���Re��)|J\�,/9���K���x��+��*hj=]�͈v +�-�d5��4إg'�q(kfE03XT�X���cYq(�_ F�uGMfX�َ.2��b���L>p��a�������#-T_'8�HW%�0��:��VF���7�l��ZyQ���s��\��p�}���l#�J�j�,C��K�fv����qD�mE������&���7 W��mw"�ޕ�_LT�2���"-K_mJni���^^���r\��2o��ڰ�����'R�A����������d��&V��@ɚza6�����g���3���c���5�\��c�|�qՇS_�L��E��{nP˚�p=g��(>��qI7+����C� �'Zt��]�9� �8u�ۢ s��K���Av�JWlL��,���G7��E�K^��Q���0HW�q��5�L6�����E���ǔ���p��������)%�J�>��A���Q]����o�PruE��O�A�G�N~�U�ܿ�� 87 | Ѷgg�w����i�߹�S��8cbP��u��X��rte��|���z�}4/}E��gɴISJ�a���i�_�uY�u�z�k� E���k~�!�$e�5��������@IDAT��^޼���nXG_}FE��рߡ�ι��z��nC�I�(���qĸ�j��XM>����B���rUW�*U���Q�^m@�3D ��� ,gO�r�"'���Ca�����,���AU�թ[�2��g_˜7�m���/�*�TN~�6 ����gt����䯬s{�E�6�(��^`��ς����T>��+�/H߿�۷V��~�/p��_��| 9�$��:=�E�%�� �dv��*���U����/&�d�U�x�p{hf����V)�>~|��4n೮q���A{�)��p}=꣎�G]:��b^}�jٸ�����4�c�X���S�#�n�Q��{��xK��9��G�Mltf8�Eù�Z;�]��j�����r������r��u<�j��Lw��������� 89 | ��{Q��,T�������c'�~�T���T��q6��0Q�,�m���T�ї�%�Yg�s�hF#=���t�b��5(۠^@�1 s ����2AHw����� F��lV�&�̦�CNNKf-r�G�fر�� �����O��������l]���>���{n>�u�xa�e��)��•B?v�����U&������г?�G��P�DpGӳm�F�Z����GZS�����3� 6���ui� "�۷>&,����D��s�)�=�+�u����+���c=�꣒m� �&ԣ�JPQ~��#�6� 91 | ge0f�I޶����N@B��f�L�/�Ф��Y�b?�/��r0�Lh1e�U�C. ��I]D�R�N�Dz�F�� ZS�� * K8�ˀ�*����d�_i��u��Qr 92 | �1�8$���'�?�9*e�!����l�V��F$4�j�U�ɺ�9B_������f��4�>Si�/����?I�N���ș�ڧ%̇����mҬE`)F%��&�{|*��B���6�Ob�g���ȳHI�;3��a�?x���� Nwq�Gv�/'���ŵ�[���� ��J���ub�ai;��6��y���H�y���Tֶ���6ҷ��5�g�k���a���M��c���p}spP=W��T��b�c��<��9��EV���Y)�ƣ"h1��I-���KLK+���>�N�4��%Խ�2Ŏ��tZ�p�Z�M��Y�ܬO}:�z��Bܑu-�0�n)=�z���so;� 93 | (��m�i�s�#�}�ӱ#[l�=�>p�<�Ọ�C�5�ۣ*�lH=����q�mD�o�Di��{�"{�RJax�$6�:k�E#3��~Ŏ>y���SϤ���18o+__�|���l|��4��胈,vLM��`2�*�qff'�N�gW���"�ĔM���(�hP��}k+-�7֙���K�Q��l"�+� 94 | ����_+�Kv ���}O ��oyQ����m�v�%�R��������w[�!�-�ab��s���FX�-��ߚ��&�=@ ~�Þ���?��� ��300���C�������5�O�Տ�Yy��Aj>���n(AP�2U�/�k�aW�i��y����{�UXN��֍��^z���S폔��[�]��9�筊��b��쟰���ܣ�yG�ۀ�U��랿���#Q�#���u�cea_x,������L!���|:�~�P�Y�}�Q}PD���t/�(�slQ+�Y�f��/?Q=zRD�6��OaZ[X=TJ]k�"��V�ǬR@���F{��Ͻ�]y�Y��K��~_1%�v���޳� ��n}�a�g,q�� y?�m�>\銠��e���n��S��p�p�=�S�����'X��J�[��3��e��[�ض%_ �E�Uc���k�6ډ� 95 | #�e�/p�K�q?�4�寤�Ǐ��h ��m��UA�p��>d�KxN�y��f�QD��զ�E�^����o�Byl=4w�R�GP�Ē�Ȯ�]��i߰qg6yoax���n�6��T����ٵ���S/��v����lm:����t���?9�P�Wy�-�Q}o�˫� 96 | �Ӳc=�� �ݏY�^��y��򧪢p�[pó�Є/U{>|�^`�n4/��ԾWW����C�>7q�)#�Q��Ԡ�v�\�*f�`cK P����v��B�������W��w��/��*��C_eW>��O}U��4�5�m��UExn=�}�瘯��#�yx^#�gh��ۺ�/Fy�ײ�W)g� 97 | *rZ=-⥢��6RڻoRΒ�>��Hj�.�\� ��9(k�"���6�D��F��?�/�\��UY�:|�mX��RxOdFj�J�[vӮ��y:�C����k��`F��WQV:gO����m�[� H�@ 98 | �w�_����w8��r��7\�K9G;��#K���_O��h�d�{�]�RCދ�6͘C˧��\/]����� ��N,X���z�*��u��6�e��ƒ۰��d�tEnpOK>�|����S�hV�)-�rq �x^4jь��n^�!��.A�Na����>�O[�f��?��R_{�L͉�ܕ�&^B����)�J�d2��'U�9|�w�#js-Qy�����t(���0ULM*Gj!H��yC��+������]Z����#��W���p͝9��Blӡ��z���U��`�-‹G0�|Tf���-[� 101 | (HM�/�!?���2K�����}jf|6���i�HJgeb�̹~U�'�T��`*��-�U l��b��i�X����6��^A��"�!�U�v��b��-?�Aqm��xM��센9�߾@(�y ��� 102 | 103 | q.��2΢�L 104 | ���ą�hIu.��R'��H!e�ѥ[�SI��s�����e�D#�ç�?����Eמo)\��#q�g?�~��_���>�Ɯ1�~�6�vo�[���z�ɕZ�&��7�A(�M�;p�,�y�=:�g��x���4� ���}�ɴm�*�-����(������g���>vtj�1�F�q �q|��\���E����~8_� � T?�������T��q�Y�`Ίe���X��V 105 | f�E�S�I�Bշ?���8�2ʚ ����3�!ѱ�w&k��^�����gW��4 �{t�����.0º�Q㇫�Yl��?7?��/��oz�����ߪ4,){扁`���[pؘ�-�A Xh��4뱵V_���^�O��AD��k m�P/%m�����}���Mm�>�Rq~x�Z5��P\��vA�� �l"{�e���� a�p"*H>D_�Au�3���������u����l8&�M ���+>� �q"���5�� $�` ��s�8+��jۂ���0�ZL�������a����\�*���K� XM���@� �|ndtB]���E���+�h�[�ɔ��n+.+�G���K�#GA@>`e3f��+��X��c�d3삥���J� 106 | ����\�+�2�}P.��@�(�A�I�TF_��O��1!"/��""� 107 | � x�L!������d�X���Gj4�ik�����i��ШתبF6[<�����-�z�[Xa�W.��� �.C��� �D�Ha~��+���N��x;�����2y�|u٣�rV_Q�En� �`��^��^~��9�߿{�5�7hd�c�&�8y�e�r����/�/d��� 108 | �vkv���Z�@�+�eW���EOM�.=�� ��X��~�cyy>[1L޴]��z<�v39��w=�D�'��ʓ��N{]p�u~Ш�i��GHj!��&n�y����C{i�/<�B)G[d����p���b'�k���۾�4q��.vbO�P�2EZ�@ ���ny4���i�jQ+�D��k���uiق~��c# �۵��m�SLl4�ԓ�ٗ`[�=~]�ω֌�'����2R� 109 | �y�%��P!A@�]`�_Xd8\��k��$�>�n��̐���!����.��f�o�0�b*�^�rb� �iP����Pv �v�6T�Y��y�i�c$h�� ���Y*����ө����� 5�кu�s�������<�dk� �ą�n��I{�Ѷ��/bw6S�PQN�:�������YAG��Q��Q�Q6��2X�`WE m;�����m{�*~�m4l�5��� ��s�� 'Q���,E�%~�:�/��0w�ّn��Z~����Z �HA@��4�DGw���^+%��º'f��4�X����%�h���x��.�(f1ۈ�q��B�� 110 | f 111 | ������g0�]v�ػ�c'����#D� �h�Y�����Ǐ��΂}{����(�a# 112 | oيBc��y��L�r 113 | �٥�+��jbO>�2��2*Q+�*�׀�|�*:���s23�T�n�;S]��n�w������'� �"�;_��ٻ(ֶT�"�ق� P��լggڳt5�f辰�2/3�v�[� 114 | /�j�@�O 115 | /g_��/�z���,>�c��΅�T�P��8��d����3��%�A�<���6��w^K�5ȡ���J� �G�XD�Δ�z%��3yy��RaZ*�5m������ޕ�KCӿ������ ���0v�P�ҫ(�WH�,�2�m������ɞ��տVk�=N�����K:1'�]�UELl �O�%��~���� 116 | `��#���eRI� U����@z������4㭶��QT�_� ��Uy�r5� 117 | g/�*�`at�C�R|�F J�+邀 P��?x���y�]Ed��8ea�]`f�(˿�~��1��p ��q�p�*QA�&yRR��(�A�y�V��ރ����ѱ#�-Q�W~y�c��<#ة����ʳ|�%�,ɜ��۔��0�^=k�U$�]}�?���չ�R7A@�X�v��y��bk�]z+�u��I-S��)����VŚh����n֢)=�����3�e�7�y�^Q�s'I��*B�՘o�X6�R� �C=E�ν$u���6IT�ڛ($�؏i���{����A�W����`��c������ߠ�k*o��Ux)����O�NM�7)�S�A�6#��r��z�=����P�M ~�5.�k�[�>�� P�9Li�O�ɇ��2�������0XcK�����e�u�/M)((������}�A_��gĸ�t�m�StL��Y�_j!�g���h���M�;�F}.:�Ծ�jS+�� TW�rs(c�7��|i��b��)f�x˗a���T�Be�B` ������_������", �⦋h�I��UU)W������ҏ����U���_~�� �KHj-T99+�S���SQzz��%�AC��p[=�Teu��+Q+�jWb��� ���B�9�[�+������g���W�+�#� T�#�R��T���gҦs��z�7HC�èӸ��}���:BV5x�J�A�;�la4k�t�Y���-A@(? 119 | ��h��͔�m 峟�"V}�Єz�Ԏ"�u�p�V�z���]�W>�2X>8�)�Y9t`�ep&#=����(';�"y�T4;����奟 )��8�j^k� i� L�eeSځdJO>By�$>?;[-)���p�J�cY|�T�Yc������ԷRWA�� P��_��� KJ�x +���=la9��1���P�B4��F�8N�3ז�"�v�2j�GA@A@� G@+��A��� �� �� �@�2h�EA@A@�`G@��`�A�� �� ��  �`�IA@A@A@�Q������ �� �� ��(��&YA@A@A@vD ��� �� �� �� � �dA@A@A �e0�{P�/�� �� �@�2h�EA@A@�`G@��`�A�� �� ��  �`�IA@A@A@�Q������ �� �� ��(��&YA@A@A@vD ��� �� �� �� � �dA@A@A �e0�{P�/�� �� �@�2h�EA@A@�`G@��`�A�� �� ��  �`�IA@A@A@�Q������ �� �� �@xy$K �r�E�DQLlL�^\dvV6���SdTEEG'T�Pu�[5�G� � �‚J;�LQu�(�n��� 120 | ]�CB=w,**"¿��U�CB(����-�Ϋ� r�(��QuZ�Yc������u����������^w�8A�6!��n���V�en�[��-f6~;��2��rs���a 121 | ����:u($����� 9�����9)8v��� ����x��*ӃlOi.����M�$�e��-�� �7��}M������,%..>�N�8�ξ� K������n��kխ_��6oL'�u���JC����'���r:a� {�:䶧i7�gЈ�tǣ7��I>J�_��K����F�Eם���R�\�{��;~���8�����TJٱ� 122 | ��U����� }�����x�Z�鷴y�_.����Q�����a�ث�����N�n�uA��`T�x:���,~-�Y��4�_�[�옿��3�uL�1����d�e�d����o�&��{c��<սNb��g����h����&Ӿ�k��9�P� �xv/YE����Z&��'��t{;0n�/?׊w���]~�j?~G9 �T�%WRdw��$`�5�7��m��%��3)f�( "3�ib�:��U1�x��ՠ!E 8���,1�"c�ꕔ��9���,�Qc�;w"E�ik�� +�f}�xO�w^����)�m{�{�n�t�;S���s��Y��2��B᭓�� �Xi:�� 123 | Ú4�����0 124 | o��J�@�! �`�a�WI͜O��o2��xP|�8**,���Lںa���L�KO�p�K㗬�;�Ѳ+���/SJ�fpǯ�p��/����CJD��%�_dd�Ś�/��zX��@vv�h=,F����8�g?�z�-���T9"�b(?'�2�� ?Ϣ�c�4��K\��?���߿r��|�Ŕ4l�ʃ�FO���P牖}`�FV�VR���-�B��jE�سl�Źw���AOuO���`���]Q�c� 7ת���5�<����$C��Z�@^�=��da e�R��Ş�0�{fr���-�̤��ܳ����;�EL�_R�/?�EF�����*<���� � /�*�-c(��BL���T�� ���,3o�F�]��"{�ǔ_�9�����3�����U+��է��$�b(�)U��t7@��tҗJ�е����<��Dq9t��� ����É�8��������zK��;��g9Î���Cdv)�@[v���(-ЖQ((�RFI � {�=�ę��ޖ�?ߕ��$=y�I���$�w��]��s�ɢ���m]��f~:ہ jU��4|��f���z�-���ڹiu�bO�?]������=C'�fГ/=N��%�t����X]�a�@wQ��tE�Rj��>&| ���4��~N!q�d-+������/\��q� ��Ȗ.-�֏��>����}�ѓ���/I���yׯ����ן!��A\-A��[�;�"��K3W������E)��ӻ��SO01�v�x�}m/����ѡ�i���s�4���@m���m3RN��i�ɚy��!����qZ9��$>����6X3���Y�eA貳(��o��P�XJ~�Fڵ�EET��"U��2��o���g | jeG��92ʹyuo��9�`<�E����Ud��S�����ݶ���q�̟7���%*SV[����u�qVRٱ�T��Z� L�:t$����7 � ���y�6/�����6Pvfy��o������攘�ްY�yzz�W��0�n�pUmA �,��Z=|��Aݍ�6�&�t�>7��ݫ��7�v�f�}\&�v���ù>�k36�z�&��$TӚ%M�^A�4����qd Q����$rh�>L�f�d끰�q��z������r�uy�٘B���l���ªf��U��E����;�8;�u줖]�Om�~!ͨ���/8Hսp�X�ې��� p�0�mXW��������蜴V��Psh��Vn�[yN՚X�u�:�G�F����'B<�ᬠ�m� F�lVi(Z�c2�a��ܽC�[�R�%{t��M���l;�UE��|Pӆ��?��;Qཿ �������"ɗ 125 | �'�KՓ�[k�>��v�׍�Cj]Ϲ ̘Jr��m��4EPo��f�,LP����1��Jc#� �E�03�`� i�8giե�J�9q�9�����Ϟ^�?#�>l2Ų� 126 | &o���&��t�M $�G���N]�d{R7�|�t�u��g�RMj�����A`�96)�˥�J��ײ�:���g���XO�R�8�g 127 | �m,i�.����'Z�����2Ʀ��~�.c� +KњT�&�J���E���J#�:��G�RU�6b&� 6t0񄴊v5����v2�y.S9���s��|n�"�,�x�Y6��� G�Sho��M�E��C��wLI����ު�=�bb���ҥ7 u�Q ��jlF�I� p- P��&�e�T�-�$��s9�C�xh�m��̚BK�M��/����u}����V����PZ=�M�~C�~�+���R����2+�ܶ[��S�������]��e¨jjV�ufO�2E)#���kK� \N�8�� 128 | �̕��C�;31�@0�4GŐ)8��/�w;�֢i�M+dbb0�6���Y��Ce'O�{���X��=�$҃��j�rSh -I���VYx�çgT�=_���x�:�O�D�3&N9���*r�3!���E_�~�ۧ?��߫��̚A�?�s���A4���=����N��" d��[����� ğ]��E~����ҋ�wwn��&}����S��^�� ɠf�٭w�*�Ÿ�`��%�u�~�����և| 129 | ��F�k��:���ù;#9�v3]8|L��á 130 | �@:�0�7�]�R�HW�����1}�1�wM��^x�P�0��3UcR�\�g��҂"R�N�'0 S�p���aU燌��Ӗ��]e�Z��O�gm��& �/&ׂ� ����<��a �x��b�F���8K��6�� $�Lg�r&��C}��];/a �����s* 131 | �U��q��yT�^�<Ћ�Y����M]��R�J+� ���-��'R �c���#�0����L����Ik�*OG���ӧyTZ��I�>_�/��/]_�r-�H`>?X�EP�yX�� O��>��?w&���4|��.,�-�Y� 132 | �i��]*$Rh#�[hꃷ�����vF�_��)i������Sp�L�؉���� ��T�p���� �f� I���`G5� pXc$�kO �z+�㶯穳zFeݥi&�0ř?�V6�� ��@^����UuI�:j[o��P[��S\�h�S������ �[%[6)M���/y'�P�^۳���z�iG��8d{hN�R����Yӈ�4e��I�18�Do���"���R ���q�9KY� E�*�n�4]=\�<�������v4��}GE��D ����</!��3UiY �%���ٳk{QQ9_qS[������ò�Z 133 | Vm�=�h�YS�F�k_����}�Nڴz�[23Ԯ������F��ͼ /���YVy���G��61*�>o��QۗzlF}J� p- ��� g--lgk��fe�Kx5�ȔΔx�Pl���s}���ӨM{?|@�o����Tu�7���5]�Ljd�3�������㜺�����M^�z�R�״�F�(����n���F}J� �A��3���Lh�<{���R����q�r�qS��-�ɻ�����2Z�O���?v���y�-����"af�S��,_��̰����e�p�"}q�뺌ǡ"�`,�|sX˧L=��g?��@� 8�q'~O/ S;��$hӌ�›Z-ڷ� ���ꈠQ����?�+B��M|T����U\�/{���/���vn�!*��YHU�ߠ �\C��Ao���GgA 134 | 0�ˈ@�Z�ۻ�C�>���ȠC�:��V�G���^�qv�szz/�uh�^E5Qb ͨk2����v�Х�J�L�Ѵ�c��9�q)o�`��Tg6�e���]�$!���z��wH� 135 | 2_�gq^�m�ә�"Dġ}�kl7�gg�i������;4Zz�u�6u������_�jM���iJ#dZ���٘�v�0�~�K��B�e�c���n{`�3��_̢vR�흢>/� !ж�˝�QT]��ʄ3�N�kZ���P�� �טW�"���<��z�:uȅ�c"U�����QM�9��lJ�9� mg�lo�9�R��QẔ5�/i����~/��]ȃ��@<��U8uS��tί�u��UC=�M�<�.�Qu�����FNlr?�7���Pk�+*5uZY��z�����JP����B!�����;h�J��E�Nu�ٛ�_z��;�p��'LS������j�첞�p�����K����^Rx�׸�P�4}"}��hv����2*\�s��SJ�$z�O��� SF�{���/ZE��O�*���YD?�^�OR�����^�b7�|9 �;��;�~U�ux�,�p�'���%��#�p��T�A9o��&��A�9�q�n}�S��-=� Nc8��^pn�h�T��;xC�tx�#�X�-�'Q�[��ȝ�!ݵ�8y~�o���:�.ץh/�u������/��^�ۉ�9��{Ϟ�T���*�J؎��9����9psT��\���t�E����3"�|��'���5��*�������~I>]��/+9� �Ci{*BB� ]}_�^��:!�3z �8���x�x�ZD�ˉ6���^ o^���u�����/h@Ja���A�s��4G�� 4 %%J+��|p.S�k�^��k�]���"!��·-sA@A@A�� :�vA@A@A@�k!����,�A@A@A�!��xȝ �� �� �B���Y&)�� �� ��#B�;A@A@A@� � ^_�LRA@A@G<o�N�B���q�� 137 | TB`����KpUTXLE�E�,��J_��IA@�L ��U)Zg��賂c���.�{QN.e�QP�p2y�Ȯ�o4m��yZ��q9�G9�Sp!�J� ��1b��3��.�1j�"�� pep�����\�y�6������^+_S9���yT��K�p�8�Z����]�[W����ƅ�����}T;�‚Bz�'���i���I-"¨C�v4�1d �hY�\=��UV��4�e(��ܖ��>����/6��B�=��J~���)�u�su��'�Ѣ�~�ԁ=����r(�6��ۇ�)�#=��/�JKJ�����k���g�"�����1���ShxsU$Q��[_�J^�^�v#�ɏ����E&�r!W���<��ȳ��ʑ��f��Sh�8�2�z 138 | u��Ͽ�?�tH����s2��Ϟ������GG�n�B&^���HB��c�eq�s�4���_�ay�-��!7 ��;e�8E��N�=�T��<�}�h��ٓP~��s�Ⱥ-T΁�51��խ3�}p��D����Nl�i�7����L&���$M� PY/��^��@�H�O �AC]��Q��=�~��UI�ʚ�Z�w���7|$y��ګ����92Y�?ߡ���R��M���H�gJ��(���L��"���`�,*^��0��'~G�>Z��̟5��ׯ!�����)��vȗ�ƍ���6���at����y9��Wօl:�� ��z=��d�Z�u�����ε�E� �2i��4U�FK^��?xġ ���9dTޔ���$�åPe����� �z�M��g{`��ƥ�W��>]�A��#�댞b�yyT���I����*/�YW�gbPǞ�|�l���T�j9��&U�Um6�����j�L����h#��vz��2��+;z�r���N�Њ�ߠa�;�j~E�V��qa=��7�Si~�G t@�i܈f�i|O.��`�[JCV\TB�m��_���>A;6��n˗����{�u���=u�V���F��R�r˺���L 'Nk�ww���ы�=Kf^X �� K篠�������T4�?y�y/D{��p�v���Ճ��ן!��:�s?�~�Se>yh�:�8f��D���v��t��=-��9J_�Qݣ|�m����IY�3h���e��\��Dqk�J�y8�W5$&�G�|Z��Ƽ�L�4���3)3�8����^��;���6���l��k�j���y�^�:�W{!��+�@�o��Ȕ��*ټ���2*;r��V,!˨ц�Ruj��i����,޴^i��gNS����G+V�O��T�a�*�ӻYnG�,;�A��Ǥ�M�-��ۦ�2�� �}���s�RN�+`,����"o��='�zrN�.�IYA�J!��V8ߐ�@IDAT�ge3ь��(���ҠYT��Nlڡ��3�ىJ��nO*��v�l�%�g���،���<�f�>���+����w��#�eǶ�����^�Etɋo�֢C�rd? �Me-��Z� �@A�n�|�p�sr����}��Ì��]G^ ���!CwSz(��ϞV)f6ͬ���B`��,liE���k�wr7�c��~@� �.c����4]� 6��nŢ�.#��;<��4��M���V]�t��)�>���#t�l&����� ?z7kAK)�� �:��dp ��{���[L�ج5�R�x��A��!��7+=���@�o����8����2(�g��Ӊg,[ L�Rc�]�K��5���x���t�h}{'�PɎ�*��߈Qm�M�C@�`���Ԉ��Ӝ��<4�\��q m߸Ӑ ���gT��=鏿�3gϣg3�Q���0�>:5M�ggf�O��VGQh��v�:�q�f�w��� ����iDT���{u��m�]�A��@����j"���vж��<3l.!@��Ko -Z�M��Tf���:v���϶�ٵ�=�;�`���m_ϵ��+�M�-_�Be�c#�*A�Vn0&�.�3��Y���-�=�Xw|�������4��"�� p!�k@Ei� 143 | Ԟ?ovU ?>O����:/òuM4hW�y�;��jZ��9ߪ�#�" 144 | }��;]�R5$o�7�C����>0��쩔L���ٻ���P#6��w��&�s�Z@?��h&��"���ԣ3�_��6��B=�u�g�kM3�e�졩��}��OK��1Ch����q�������c�-�:�M���/�>K�PD�>b{��v&b*(�ܟ��/�t���%� /g�q�dB�yH��j=�^��ˮ�C�ױ騅�$�| ����A��bb�wI�o�M�ΤS�=��ȳ�rpXg3��s<-7��f?���9u��N�&�񜺆fq� � ���ŗJ� �uZ$�!hGEA��A ��?�L�3>��ϝ�����=�J�Ӎ���ٛ�'ޤ�07U���T���� �Bj�s�A�M{� oW5�lB_�ް�W 145 | ���K����s@w¹�Xv c$��&��u���T���/��lۏ�B��JS�)��]���fa$ΞCq�9�g /��i���LԾ����9F���P&�g|�S��y���u�I�h��&��w�^ͼS�n�5�c��)����L;OIp&S��F�x\�T�N�ʜ<�֟��C-[����:�� �@$��O�ź�~LA*#�M�-~"[nhDOۏD�n_����5ZD����������゚��;�q;3��f{9�g{z].��G���L���SP6�WD�MCq0��Ij]���M�Ci{*BB���Ȧ; ySG��� T�1Ú5oFA�A����d��� p��y?�<�eel�c'a�h��l@�@ �g�0~!��o!<�Bۈq�d" ��r�'XQP��C�b97��ʘ 146 | ����"!� ���#�� �(��s�� &�'�줼3�)aH�5�f"�()�A@� B� �ҵ ���C ��i�&$�8�����$�� 442(d��!�A@�D �� �� �" ��]1�A@A@A@�z� ^�_�LPA@A@W� �b")�� �� �� \���b�� �� �� �t�DRA@A@A@��2x��2AA@A@A@\2芉��� �� �� p�# d���e��� �� �� �" d���N)).!��z��H�� �� �� \*� 6 �����~=��t���i� Г�>M'��l�.]S���(���K�A5-��74�Ք :q4��r�-#��� P7���_��C.�����tf�A��ƒ���(}�&5�uc��C.�o�NY��L.�I� �5�]�w-$d��4YϞi�S-�ɦ��G���� l�oDp�W���ǧ�sަO�K�Cz�z��K��S�OQyy ُ˥��� V��������S�+�R�1b��oRg&���Am��)#)4�9��p�g�iߎ��.=:Ѥi��uڞ���_?�3'�R�4r�p2���+�Vm�lì4*.��.�@��f9a8u�ֱr����mE}�Ri�g槳�����PF3>����"����=��*�֋���~y��<�/��锓�c���;� A�*F�̞4�5�*8�I>l�4�jٹ=�Ҷ�����#��5�F ��^�k���΢��Pia��EQ�m�ɷ����-\Nع�p���)apo�9��{�v}���Ғ����:��{�Mʵ �d��J�3��R& ^��j�E+��OJ�(��:*/*���s�Y���B�}�w�d*;}�J6�'2��t���ޓ� �•K�d�vn��L��Χk7��ٛ�V��<�����W���7�F��R�{'�m��[�P��M�}����h��{,%ϸx�\?�<||�p�d 151 | #�)\�#��Z�wRWʛ�y����]\��A�<���M�k�<*c�X�gB�;Q]�$[ϟ%O/�?Ϸ�Jϟ7��ǎp�"�$���d����2B�x�����Cij�Y����O��n /AG~Y�!��ME���cU�q1���D~CF�+l*x�9�C��8�n*�ϧ�s�z�y���w���ۻ����� �����Փ�:2/dQHh0�n̺Ԫ˜�\eBF����zt���w�'o~N�F�Wd�9K1D-o/|����I�� ����Tc�v�v��P��mh�Ա�ܫ ���� ��L�Z���߬���~R�9_�TuJI�i���^ח>�~Ap�Ň~&�1��>x�S:z�*�O���Н~��T5&wc�\�~=_�5�w�\&}����goIv����c��bZGۇ�y �~ M�k<�_@������b��t�͚ ˙�(O�s�P��w� ��<���漯��AZK�}|?��&���0O�������|L�VeA�AX5�}/�FߟVF>����Y��W־5�y`*u�0J?�y���PN��v��IM�5�����s��L�me�8E�+78��f��[wQ�a��;��l�m@�)��S$Q���XA��7��S��w�Xs&K�i�w ��ȁ��R�۷`�<�q�d��&VᆾA���|SnK�-Ty��0�셵k��bR��ѵ[D/��a 152 | g�[�V��D�Hvw���VEA����p;g��= �8�&ߠoR��&�ȁO���da��y�T�m3��?Z�jmR��7� �e�x���?�l�o\Of&[��o%���d=s���-&��#(`�-���,y3�,ٹ� 153 | ZH>�*�W�掚�[E*�� �)=��̑�T�} ��B#�q��^���QsM؃5y�i���J��th5A��V[&� ��$� _Jj������ �_B��tݸaJSz��4��$����J{��5K�3Ѵ�!lZ�U��Ĥ� 154 | �I�y�V���O���mA��"���Rx!�B�($>�Z%%��϶�s�I\��#�e�vԎ�^~��w6;�9�o�?oH��f�����Բc[ 155 | OlC�� �s�5��])�|ot�d�e��*9�b�|��X��>A��;�Q�Є���4p�L�P���At=�ǃ��9cr-4�����I�-�Ե^Ç4��`&��*�?4�2��B��#Ia�>2vT��$�_dJ' 156 | m��lAf6�SO�s�w�a2���c"k��+�{�`��OV>?X�f%y�OTZ5��m�% �i�fʗɣ)0���B���&s[21y�JhKމ��3&������y�� 157 | �@k哔B���cY�����P@h! ������(ݿOi#�;uQ����}9>��Jo��y�IJ����Z�w����Z�!��8Et��Nt,�0�1��?��|X�M�+ ��J;g����t�S/<�\7,� "�ݾk!��n ?Q��w�ծ�"˦� 2GD�fä�{F��1���c>��_��#�Y�*R��fL�2�e)�޴ѹ��>�5h���� O�U%_���86��Q��/���6��&? �F��P6�w��|Ek�Bv��xP������1k�L����,&���!�;�����s��FD�tפ=�t7_{!�4ч&���2�v�zE.���|�Hڂ�4i��Z�R��!��"0;w��@<٬�+�mn��*e�P+�iB�fe-���|�D�G�-�3hЖiR��Y+��2�l�!� 158 | 3s��V�I@xe�P�Eu��W�_���B֮A��s��ư֯��Y����"h毋�}]U�� 159 | ^c�K��Js����P@�P�� ��lf�uꃏ��U�@���hs٬�#�B�E��n���r-4y�$f�>�z�Ǝ͟�����g�V���9�D� 160 | �~K��I���94�zT�a� 161 | �wh�@,a����+h��l> �(,Pھl��A���d��_6%-�a>� �4��SnS���v�`j�L5�u�r���Zm5�<�� +����r��1kIO�W\���r}����6-׏��Y�P9����1JKi��9P�����y\����\oML͚)�Y�^>k�����c�d�����C�7�T����e�r�䞝�>O���)���8��kOR��h��s'��ݏNs(�A$.���� ���������-�� JFR�1� �B_oB�0���2t� ���/��砑���3��l����# ���E�"Õ�&�{���I>�� `���x�Xl�l�2[��g�p>�����3o( ��&'���3�h�+O)���x�2���> y���1� -f~���k5��������|@x�TZ=|�.�x}��O&�לI��gS���y����Tg�LlJZ��֤�ɡ��0�Dw�B{�Lt�/(b^�fS�}�*�n���r-\-�,4�՗�2�lA�Ŭуf � �?M�v�����w�.lv�����ge�&������x��LH�v`��/vfS1{�2 ��߽W[����CyU�i�C#"�o����y������ k#%{�P9k��Et���Q�=���Z_<|ٔ�r&�����\J�n���Ze�HX�),+ aEA�:;X��R���������q��?Ƒ���W����c0�wÍg��>D0���iŦ�a�k�%���N\�;|��Cu?&.'���Б�M�Y,)*Q��-��Vg�4rx��S���� -#���X[w�Pk�C���v�]����NB+�O�x[ 4�2�k���ӝe����׆MK��������4^��H?h��m!ty�V`� "��h8���&����~���������;���v� 162 | WO]f&H�(����т�L#)��a,�:�f3�h��Yx���p�Rp>K}� DT }���0���(��q � g8 K�y���v V�& �g��[8�Ѥ9���2A�?wA� L3���L�U�MYi�&0 �~R�E�� 1tj�~&�j��7�p �(����!NL���|��W��f�����z ���9�=A��!P��@�pf���R���%"���I���Y'gK�5�Ǘ�O%ʤ3��;(�7O+sG�{��+''pZ�v`n�;�ZU� sRT�pyw��d�Y;�sm�� ZG8��QuL͂U�X;���{5���V�lݤ�Cyh*�;���'k�|6�&�/��h?��A�F���N�ϘY+�TbmUg0w��ƫ<'G}�z��Џ�T�o��4p�b凙�w����B���' ��ڪ�v�/nzL���}�#�(�����;�^��?)����0ޭ��/�N��= �f����'�N]�_�AbsS�4ăG*{ǜ�@���� LT�!���+IM+��.��#��L��l�p&}k��Z��ƈ2v�a��r';�y홷�WT8����'_|̡m���f�Y�ブ��ҟ���d�[�T�Y����Ͻ�f��l ��B=+�.���Fz�o��/>[ْ"�ɂ���U�Υ� ��#�b��X��)3j�8��٠'~�F��������?Z�����-~j�7H���i+{E��ZQ����,ڇwQ8�Y��'jM�:�����������>�O�6C���~���|�?<������@{��\w�ҬFtiO� go��� B�s��7/R>��Ҫ�W��F���O����| {����'�����7�7|��/s(����(�}�|���=���C�^CS��3��?q6����u�P��Ox-^��|zT�����CeP�_�Q�2q�wLWռ���|vj�񙂚��ʨ��2v�}^� 163 | rt�nk�l�Af��9~��pp8�R�����s�ٯ�l����&����� T0o�IW�[ho������G�n/>w ͯco��c'P��*ϮP��7���;���T��u��7%54` 164 | ��;�1?�y6���y0ń��]��,�g�@8!xh��Kxu~ҏˈj��D������&�i����4&��:-����thk3.��+O�A=�wS^J�4��T���O�OKǜ 165 | 󋘠��Kh��)\�@�G�S�L������.�������|Q� �'3�U�y��^J����;��o�� oz��D����Z_��f� �>l��,0������}�DU��2&]F�Ǯ40�B:hmBˊ�����ZMxG�476��w�qh�a~8�h4fxqE� ��z�&����Ne�0����qVp8!�{""\ � H�]Cf0�/��ڹ��&��XϝS�Ne���T5�9�(c�cq]KP�h�*u^Ј���[�+Fu�tŃ�۸{�3�JR�b ��ɪ��l�����խ/��o���CW�4������u�D �� 166 | �x4������C�#�6�23�H4���DM�K�%�)��i@���B��� �4}>�k3.hk+5��];�%Zu!�g2�rx�}����o8 }F}̩��ӷ#ׂ@SC@�O?v<���C9#����_�G[����s&{Z���D9��V��'/^FbD�P�f"�<�%ԝ�Dd/#���Dyg�1?ws��خmX��}�X 167 | +LK5�n��ƨՕOA�jC!"j8��I��b�,�`«%ξi��x�M4�*"4��(2 168 | ���@+X�ͼ�횜�<����~��V'p�S[1����*dm�r��g��%Qh,��X��� �m3�Q 169 | �� P�@ T����*�"H_�� ��`'26Sߪ�r%�E�$ 170 | }����ۨ�����N��VM��@��C؉�A.����yb&z5����H>4�y���k"�� p�@�z�B"�^c�Z�i�4�9'N)sO��h*�n 171 | ���]��a��/�X[�^8��V�W���5�r�Gc����LTȠ+6�"�� �� ��U��F�n{�����A@A@A@2與� �� p�#o���SDA@�m� 6����� B��+%i{џ�Ju/� 172 | �@#��}���_�k���5X�}NtGL���R�����f��`�5Ɔ�ق����&c�&�@� OS�\A�8cx�%�lʟ�����o_�`#�����{���%�!b"�|}��yUn��ێ��Ɓ���P\�4��<�x�ժ&�z ) �^C����Ұk���A@h�o�BE��5�*��i�n����u�"��Z� 173 | _�n�|�1�\D `�ߺn{m��2�� p �w�%�<�����hd��� 4 174 | �'�ڋ� !� ���+��.��v�c�OP��v4������I�88���T���#�����h�� T�gx���ĩcU��VS��JLnO%%�4��Y�g� 175 | �>r�p�ܭ�}ƱS���s�$�Zh�M�����|����k��S�����nU�����y�z���oY��6��L�=�3��㜥���EQ-)';���`�޺��vlC7N�B6�����h�OkոSz'Ә��W��|��M����7q�f3=����Y<��'�Pt|�r�$�~���l��mE}�Rm�<�����G'�O��/�����QDtK�4m�� ��������ɪ�Z���Խ�f�OR��4��h��J���!�n��D�� ��=�;Dމ#�:��?�]͋"]�T>�E�l�j�Y�M�� /?_�ʤ0�e�J?�v��y�݈�������m�9.�k�"�?w����Sq^�9;�Bg����ͨ��� � [�;K����E)���K?��J:��w�s��?,�R���|&��ݸ��)�_{^qnm�j.]8t���m��S��?���EGб����ۍ���j������sm��Ui繍3�ӀG�&kq��v���oJ��f� i���i��ޥ�N#�����j��In|�^�x�{h�!t��w=�����6�=��ªӘ�ْ�ΜS��>~�L�7$�g2!/��HZ�N���c�C|۾�C� a;�]CzN��|��~����H�7����b 176 | l���U ț Јȟ���#ȗ�Cʎ�8��)p�݊X,�C�����Ϗ��v'�޶5"�����MP��[���= 177 | �u*���Qɦ��tT�oyw�I~���>}��7����OлC'=�i�<�y%v"ˈQ���K�;�r;�&L͂�o�u�պ ��K�ϡ2&~&��D�ᣨp�b*ٱ�**��z�,yuHT��^��[�����ڶ'��7���Dy3�"�VQ*"� ����V�r�gg���)<�>�U�Q=wc-Z�\�seǎP9�~zuN"�!#��ӓʋ 178 | �p�<*;����LhC�c'���K�z�,��"߾�ɻK�ڋ���Y��E0���|�W��[�����cgX���l�J�ͤ��')���樲����j>�G�Ѵo%�-L^�d�5|��|�xڽm/}��LUd�{� ٹ���55����T�_Heee�\b��쑩4��( 0�����������%H�&��p��v�0a���-�����Iu�x�REqce������_�N�e*�����w�'o~N�F�� w���� C�r&� Qco����6��J��>{ު�~��T����|50(��}�P����VS)׳Z��Ͽ�ŵ����YH�`z�7��BYAY�I�*e�Ϣ=��i���>��x@�A���J�����w����>�;�����84,7�@BD��ȁ��n ��:�#Z�Ə�!Ĩ�v��IM�5����x�.���@����zϭԲs{�O��&-�0��„&B���@ 179 | ASn�M�� *����*pz����L�6��8��w�<-~�-E.��tӚS��?��ǜC=Hද�ҩ��u���T����D�������h�L���n)}�F����$��vO1��C��_g��Q9�����)��z�mvb�reL.�1i����TJ�r#Y��B���<4p�p�����m&��B{w�'h A2A7��L�� �F�K�N�|?,�Н5��ik1Q gw�A=Uپ|�{���7ܣN,�8����u��Niw��Q/ ^�W�?��Y���\?�p]/�R�X��Ԇnf�"H�����n��� 183 | c˨p�i��sז��C� ��}MIM���mG�!4bL����1O 184 | l����9�uu;�ZvjG��'?^�Υ������e� ]o�Z�89�k����eb �a�[ư���5\�U�����+��,*�ΰ& rb�.&�]U[��յ/oA�[���L��(�v���ޡ�� 185 | ��jd�Y��K�c�=}�^=�_O����cj)M���kj�ވӅ��n�y��U�>����?喱J���loӘ�*��&�Z���j�G k1n_.c a�� 3�-'�`�Z� �ds�L�L-:$(�%t��BË�dc��ӝ��jDDh*�t�A��)�)�փ@���Y��G�L���a� 4k��7����a��Or 186 | yFE����0AL"�6o.�����U^�Y�a�\�jӎ�Q1�Y�2��$�c�A�hnJ�����mt�Yz�0��5#&�Xof�[�D �8��Շw�d*K��:�7ϘX�F�Ly�Or7o�j�+E��e�ڵg����d�.�j�vz�ƑwR��O��I��H^�XKX������y� ������D.1�Hk���Cl.�Nr��U��5�e3)�����\�}6��kHvf��ۜ������Գ3-��G�a���wXo��O�5J�J�$8 L>���$������� O�U�_��긶��U�=LMa��������x���}F�{�ڥ�+).Q$l��L4�C-��0��@k�4��z�:�h0��a3-?�͜ʹ]3k:0VA�jB2�6��[���=���!'�˃�) <�VP���"T�f��Q��ӊ��f�H �e �Frt��N��km_/�rж��z�']�����`,���� �٭3]��ZvlK�l������܏��6߄v�&a�f�����|�)��s9�[F�fy�b�+��*� mpu�i,�J��.x��F$'Rϻ�8hg�ȅ �0��yfgeJ ��P�}EI1�����dM�-�76�8mP��p�� f���y�V>c�3��a���7��Ⳋ�B>�m��y\�l�y��+ LLA"�* ���6���K����d�M�X�Y����g���mֹ?��gO�� �H�"P�W�aە�*Ⱦ��d��[<|�|�� 187 | �1�����n�'���7�aШ�V�KK^���u�Z1)� 188 | >={S���T��kkq����- x�����JBXQP��^�y��.����=�d0��y\%�)mc�_>JLpJvm�qk3[s9 L]��wT1-σM��*幹L���[ucu�6�x]A��h�E���OSP���LlEG{��k��n �N� +7Q�μK�FڷV��-�� 189 | 4s΂6�;L�1��˟v��e���3M`� ��yAh�b"N^`��ۦ��ѯ��zɽj&�0�<������;޾n4kc&��+)*��?��L3>a�YTPD��-��l" 190 | �gm#�� ���)��S�O+�:���{�;|`>�� 191 | �WĤ�$�q� A�ZAd'� �����d��|�3���`nZʚ7��׬���+c/xE9� 192 | F@h���D*g&�sS� ��:S�P� 193 | fl�c'��P�"ML*�le���4"�����y'ʣ]hӐV[A�РZ���-{�j5���5����3k�Us6���Ō�6y+����]԰�FV��a��ffr�l�;��i�3�*�]�w 194 | M(4�(-����s�"�@SB��m;vt`U��@ !�-�Y�…�EyN�JRd �8k�9?)Ϫ�o�j%���FEY�2 �yDMʳ��x� 195 | 3v"� 4�V���B� �����"�<���Ʋvd�(�˺u�R�XUoX/`>Z�u�::��a�e����]�����f��tEnv���B�# ����W�&�0m^�|�����'^x�m; =;6���O|��x�>($��W.����m�*{]2kż�����{��q����M0_f�N*�p"�S�n�0u&�׀����QG(o����D[ �� \��j�c�N�� d��k���1>s�"�~d���Y�u��MJ�4R����nq.J����Nn�UL�� a �<���Wd�?{xk }�E��1���“�^��s�8K�Э�V�8˨ �R���A�����0��}�$Z�Ϗi���S1_�"�|�q2y��恠$�4F3eG0�xW���̢�)�!��="�{0oliR]�������`&�0SE;Չ�=����o�[����m�2q����g[gmm�5H9}�߽�Q�K}�9�U_�ܜֿfҾ����� �~�(��0�������45.I8���,�^�M �p~��I�gD�o�����S��^��;u�m�5�w�p��Ɂ�粹<ׁ9�~�������~�ɲ��ϼ�>� 197 | 6K�䳃������ XK�Yi�Їe�x����|���=�f�������3U�)��?����"��Sț��V'([̎n`�ZQ\��fq��H��"��ϯ�Y�2v����|����b�A�>hԅCZ}��<��>��2���4���� LE. 198 | �Ci{*BB��a��`���){���^4�� b�TɁ�M����?�����g�*�@�sL�젖���q&.�:�]s1�8��2��Xmg��ҏ�]]�P�;JF���oP����y�@������(<�y�t'��A]b���UC`.�,���,�ߏ��Ũ���H����0�4꫌��ܵ��a� -���kT^�O���S�i}��v�.4{e��+��R�}v�����&����:�x� ���0 ��ğ(i� �p8;��~� �'4��G�'S�5�N���F�_Dh����mE���9��=��R/J���q4n�@T�%�>;�ы:���G��Z-&���x{ZF�v�m]W��Ԇ�d �n�5Tc�<'[���|����d�e�N���Ь�)�I�D9u�A{�ND�&_?��P^�����Ѫ+D��cAi�:qJSX�թ��-�tu%�h����t����$�FDR#D���7�������&:\�\��Z�BU�i�� ��?�T���+��/G������p{������M�l'�n�NƂ�,�W3�kV� h�X�&"�G@�Dk�U�Jf�C��C �� 199 | ����^8f����������^K� 200 | ���C�{��U��] �Tr2�IDAT3TĊ��x��6�x���lLD��(;z�5ml!š#DA�f43Q!�5c%%A@A@A@�j�Ƞ���U3M�� �� �� ��B�P�i��o�"�� \j�uRŬ��I��� �� Ш2x���e@����~�&��̡��zE�i�n8eeVd��b����ĵ��4�1�|�[�nSq��8���m9��2G�m&+;���O[p��<wcGLQ8Q�)�Z/�z��Y�Yii;�l�w����� 202 | A@h$�ﱑ|2 wt��eJ�8v�:w�X#�L�i�n�|�1�p��!�є����D���;s������42�O\�&�q�BF!�� �T2؀�\��#4�yt��y 203 | ��𓦍���Ѕ��4�s���*`|�!����Ӻ��x'�đ���;Y�ür��s���3E��3�����#-��������[)�Es��t� 204 | ��+��7���+am�L6oܿ3��@#' W�s�Oky,���{������!����]���Ud6�)��c�����;����^�N���x��|�j�����؄hZ�|�9��CO=@+��6� ��ܞ�ݒ�.ݠBG`�Wn����("�%M�s�� WmU�GT\�jÓ�����t���pԀضa'��a�³9�Me|�XZ�`y�\��<��gP�4r�p2�M�f|��u�B�B(7;���I�. S�lz왷����J�og9A��;�+�-_���gl�C��{Q��Ӿ�K����LRJUܺ�7�!/?_ڷp9����sҩ�B�֗�s����k�ڭ��{:������Yt�h+׷;�1@�)�8x۾�C�9��7o�1�bz%��3�Ҟ�?� �a�4i��ES��Sth�:�V�A�w�Sa!�L�����q�7��]���3�)(�%u�u�W��Za���5k�s����Sq^��E�ư��o)�Cܽ 3�ֽ�_�z�j��f��Q�_M'm���oSA�1�^]��ۿ�d.9uF}'!qQT��C>���wHͫӘ�j���� 205 | :�ߣ���Z�{w�Eu.~�ɞ��}' ����+����B�����V��z{om����Vm��j�V�k�]\QA�}_¾� KV��&y�h����I�yg�̜��晳Lz;�s�D��|y���I���������[�Ě��~2Ǎp��(+��y_%� �����+hز��z ��1m�IK܋��)%z�Q�i�o�&ѩ-���a���� 206 | ���B�WS�`�B��p�]|��^�۝/IW�w�[ˆ�/=/�}�����N����%>��[�h�z9���|��b�y_|&�{vKT���p��ݴ��͖�ys$qd�^��R�x�$^v��2ky�7o��$i��ݨ�۶���w6,�'����u$銱Q�f����ӏ]��8��<��O��9 ���u�M��/�@��K_�n�E���������{Ir����B����\�5j�p���Y� 207 | �侟<( �4��M�� *���/T(���݂ �����u�]Y�����X�٘�GI׬.�_>*�ϐK���|��r��HF�twC�>{�|��4�=��\y��.�|��w�f�Y"/h�]��T��3�m�~���T��^"#�]�ַ k׎�nږ[P|�u��~�zrϏ��<~����W�yíw�}X�|���{t�G�}B��sL���st;�+6.F_<@�5Njթ)/=�[�u�t�w~����kʒ���2 ���uv�]:H�i�J�tRzf���j3���Y]]��5�_����s����se��.9O����y���8w�6ٲp�tz�g˜�t�ӺO%m� �5�~��iӜ�n?��.�����/;V�q�|���.���yV�̘�w���_>��4h�J���R�7�/���ťe�j��Kk!�n�J������ ݥ�.�VS���M�絣�C�o�}��zvsfyk�h*Y7�u�U�> �T]4� �.p�m�@x��Ubn�6/X��,m�jǞ���9���"��yE���¼sR_��;'����i��^z�`��q,~�c�-f/�E�?�vCJ��K��|ɞ:�- ��ؠ�t��r ߧ�ڹ��z���CB�3���(P��J}2E����GJdr�2��E �ЧS$��@Z�?{�[^�c�.["q��J��FI�-r����zd�M�B�'5����͛����HT��.�,ڶխZ�n��\.QMS��. �`�K�s���V�I� �h`�J/�J���ק\pZ��m��e����F�ݕy�"��%IW���:� ��`�-�׷��$�W�'�V^~�����N���):6��;��Z�۷�ળ4oU��\�n-\�HJ�ڱ�.�[�`�4њ��Z�TK_vl�NV�g5ev�2�ڑҾs[W�x^i�ZT�Y��>״f�: �K��CWq�tV��1���w�`��-�58L�'�7�����/��.=ҥ�9Yey�&ςW�9a�,ӲZ������%�����;I_�^2oiY>��֡��H;� ��������C\������?G�a��w�}�� նq�e�i X,���w�c��2G��#����҂���$w�������f�j>m�5����p��e�c��lY����M2�@����:�]M�հ%֫�!6�����Mk�t^��.س�9 $ul��� ����x��K��o-M3;��[pi5j���V����� :a���K��MR�wv�g�|���i�ҨS[ &�K�����[��5e�tم���t�%R����+��qe��Ѷi5rV�e��iDz�Zy!W u�xvVk�ru�{��Uk��Iu�PKՕ�I��e��9������|;��u��R���i��]�s����wNb�]��wN�j&���^Z��Ы��+�G���c�Z���{�u�Fu�j+ۧ����L�QR�Yc�����4g�@��5�9C�dJL��:ݥ̣@����.nYl���!����[�JL�6�*M��/Ѕ���%V�۩�[7��@ ���@,:�� -���;Kd��n6q�z���;�h�� 208 | ÂӸ��hM�D��J¹HLjK��:�j����ݕY���\"b˻,]���*q�$�Y��v�*��IRU�m�'c^.���h&z�̣��]M�ÿz�<��x��oTO֮Z/O?��6���f�ښXk�XY��?[~�O�[M�!}�n7�)K2��۾e�̘:��n��Ɣ�b�����5n1G��@5t�X�g� `3��?�� �����G���ۛ�6�l���/U�[�h������D�a74a,��-���������Aj�k�k6ា���o?k�ɯ5_%!p� h3I ���- 209 | �����<�j�����瞵ڜJk���j��J[Q�����Ӌ�6�, Vm{Qz}�Ӧ�V[���y���/Ik�,Plܹ<+�k���]z}��%ڒ��u�h�e���Be��*Cm"�����iM\��r�k��+{��Z���X�o��i?�/ |Q��#�ؽd�d�-���}9RG��V�B�Y��/���W ַ�`���{��&H P*P�M&���V�a�x��6���������.4�5�,>���/9x@�B�bF֪#����*�={�ڸ��z��Džfu�XL�t0ZЩO��}�?W"�_+#�z��,����7ٱF�hyT������)3�-����[vwf�������W��ק�f���c� ��p��*!��*Y� 210 | _���)��(�e��5o���35���[���!��'�î*�R��-�Z��.r}�����$���ow�4�&Z��y�5'z�f��%y�nӧ"��}X���W��;%U����Қ�V��ڂx �=���gg��f�y���y��7�d>�C* �"c��E�S�#�T��g��[�p�+��X ����ue#m�f��A��,Z�[�'x�q��_/Y�[Y����� ����Z����s�Ul�W���\�����+R�����S/�`@��-���;e�C�t��Ϧ˦�������Y/U��έ�z#1�:�8*�6͛g�� �gw�ò�8���^��ʶk)�c�����kF�M2cڴ�V+�����JɁ۵���O���i�5���{Ē}��#YMceՔ�T��(.YN�@�*�S���y�h�q��$Q������d�]:��>p��Ժ]K٣3�H��M�OR�$���\�m/YSѷ^~�5�l���Y���h��5e�I�&\ z�b�Ix�'u�����_�,���,]��۬��>.^�=�;��X���+�d��V��z�_��I��~�=�/�&X'�Nt޶d����"#"]_H������S�w;fk�j����)T�������"PW�=[0f}���� �R��,'r����% ��.Z��.�5����X�-Iz-��D�ҿ�( ����Y��:iMF-Y c�l KE:xJ���Y�eV�W�yӲ�/ �j��B m`۶�Z �w-���\�j=��@4��4P-����U�&Z �m� 211 | m��٭�L����Ɲ;����+F���6n>��aXs\'V�wvLqɉ��7�n�.��JI���`��z��y�������� ���M!�ϯ�b����M[MYцu�� Q JDb�Fp�{�srJ��鵧`�78���'�:�����1 W��Z;m�e��`.��Jk�-����)���'��z�]��6��r�泾�Z�ҁb��{��%�讣ZY����Q~<���u����)\��, <���=����~��C>mk�N�@�#��[�3b�O�Ӎ�i������oow�5d�y��#��j�jiB/ٍ�^�N��e�ܫ�D��z[fMB'��*���^��eY�z���1��G�,ـ)��q�\��7XV���S�ȫ����������6�2��?w���w_ϓ93�s��V���w��?pHY���i�������_-]�����ٻ�L��'2H�&�fLM��ާ}9�}x��=��؀6�}�g�;o�N]KoȬ ���j�wm֔�M�6�O�?o�Q6*�����]�ɧ�Nu��4h\_�����a� X3Βc�/3휾b/>�|������#p۵�(�I���[�a�e�&<٠1�t�k�ٴ[��l�V�>�r�������_�@-^k����uѾ~S��UZK��� 212 | Z����OR�#���3s�0����9�h��f�ȡ6X���^p���[��.=t`/E���1�dڣu7_6P��LZ�౺2X�����=i֭4l�A� i`g� �Lz� O֏r�����-wI��$*���ֵr{5 �gɶ�+��;~��F����׎q�7÷����~��o��m��}z��N�}i�k�:m�5I�-1m;HԜoe��q#�ZP������r���}�^�����o/q�h�����X����wKIQ�D֬%I�u�c�wt#w����b)�=��������6J����l帮�$��/]_@�9�]b32]9������[O.*��>Q���Sc�(�L�!��[HL��������%����}�WT������\D�C#5xs���cg�l����$_?�����(��7 Dd�ZZRG��ߴ�3j7��h�B��W9��}��U×�~�~m9�Gς�Л/���j�9�����~�jC��M[?Dk�����W��7���>u�LG�&�5��Nx*�/��n�^�-�FU�����#t{{��� ?Vk.j���'��.YkfC����g]�*`}̬�,N�Z�Lz��_���Mt��X��� �f��F�чU��Q�:���$z���~�5�����^��ר��#-Ҡ�%K��ٶ�֫$�ul?6@M��V6�K���[v`מ��xl�ո���'�,�57 7�l;Ez�-֧�~΍5��Ҧi��q5�xڽ@�te�gg�@�^�,E��z�%�*�|5ʯ�&�.ڬ<���\�Ll%4YsM@�i�K��$2d�(o�t�_��n �^a:�鿸�Ђ��d׹���\~����f��1eMR����t�ZX��+��[U�m��;�Me�B����V��}۳g�P3x 213 | ��f%��/|��- _��A�QHÓ�^����n�ce�t�6���>��҄�g}��x��@ж{"�-Gef���IJ>����؅��i�*`A[e����.p�,���u0AG[�,����e^ h�,P�j�*ێͳ��)�7����c��[����k����`��������l�t�z|G�L�,��j 214 | ��Bo��i���>��<� ����2���g��V���nv�L�p�Q��u.Bk$+KVK�'Uu�V���{�?Q;AO��~ݞ�������E�����3t�q�`5@@��4�"mfZ���^YQU��5X���E��&T� 215 | ��%@3Q_l�?�5��w������J[6n�m�e}I ��� 216 | l_�Z�h&���Nv{���^�@���$?� 217 | {���8I�����w�}�D�SU*Z�� 218 | Y�����1���f��U 1@@8�`��U)��H � � p� 219 |  ��g��B@@� ��LY������ض��+ϼ.���)�&C*عj�{ }e˘� ����&����������@��e�$!���-�n�Y>�S�p`�n���U����^���N�0�/ � p�'�ⵓ�'[@8C���$��S��r���Uk+l��;o��D�S���@N���)@�Wnb�_IdD�,]�\vn�%CF�/��re���p�a�t�EҭOWW��^~O�/Z)����[ʘ�FIB��7P �],S?�&W�4ֽ���Ͼ���t�|��3dؕu83>���̐������v�[g������wH֠2d��)�g/�i-�6o�� ����äe��߯$:*ʕqӺ-�l��7_|Gvl�)5k'K��W�Q}W�M��[/�붗Ҳ�\u��NM���_�,��ָ�k��S.U� [�:0f �@�@�a�^}Wvg�{1yj����~n��g_��-������������K��L6̚'%���Ϸn�&���sd��u���e��]R�i#�6�2I�k��)_��ny�^RzfH��H����Ҷ%+e�䏤��!���I���o��� 220 | �o��o�8\T$;W�����j�J~�W6+G��K��=���*�[�������E��I���e�'�?���\��n��Ƣ�t�r�{=Fu�Pp���٥�Zc�jH� J�^n�K��ĕ��P�$�{��xu��m�W�5_�R��R�n-I1Dj�4�����������(�f�[o�m�IDD�,y�Sg��2�巜k�<���L2ǍP�5�a�|��� � �3���\��q��!G�$ ���*@3���3�>{� �_<@�]5T�r�S�zY�\���9:�O��$��AK �4�Q�����A8��o*�i��;I ���Es�� ��$�.���Oߙ���i�ܥ�ܟ�..�'���j�=��ބ��QTX�e(W\3B�}�CY2�[#V���G�y���U��r��ߗ��md������~�;��M�R 221 | �y�3�����Oe���J ���hi�Ҹ�`�F����k��/Gz\s� ���l]��mgӜ��ϞD��yɂ9 ���c�K�R��ǧ��I���v�DEG�7Ͼ��n�&��.qAR��#%����e�Ros.pl9�� pl{]���^�^��)�Ɩ��\����e΋o�����Q���2[�}PT]9��9�����~l^~�~��]K�Ϝ+�4�p�y�M˹g�&����e����}�uW��<+SfL������5\ �Q�����گ��]H�&N������q���,��Ӏ҂՝+ָ�VO��~���lƚ�f��ݦ�j���q�ɾM[e���.(\��<���١���EˤN����� �� ����iҡK;�ګ� �һu�6�\��\+I6k͙���fI�f ���r �4�B��o��;�E�fuq�g����<�����;��K�籕f}���;���� �e�T�nR�$��CF{��N�<�Y�ji���Q���V��㤳.[:�ػL,���Vlyh���TÚM���j�_9T��p7�+����}�H�M��\<�4�ܲa��q�}���|H �O�? V�Os����ە��E ��n�/�oZ��d Z���Z�D ������@�8��lѫ�ds�û�������5�]���ÚծЁx�iP{�y�5�`��o��Hj`�ڷ�؀:u[�h��f&kS�|����J��m.H>�'G�������}d���� �H���v�' �B0x��O��%�N��<����ߞ�ϛ�u��`;��y�'2g�|yT���y�n���8��.CG)SHH( m����ݽ�l��&,(��N��ȝ���B���� ��X]���B#�]*��û%%U�%�۬Mb/vYl��z�s������X�i���?�嚒Ι9_���Y����j%���D�c D�ĸ�����Y�����h���u5�}o,����<�/�����<�r�C��O�사փz�ɂ9 伔���7?�ӎ�S��g5�^��N����k�C,�����I�m�J����f��R�SxΝ7i g�۷���8���z����拉�������:��Vp�%��}� {�.�\��t ��M��˷�7rŇ_���jcI �x�zs� �� �b7"%�_�P�Yh �b��ߛ8��/|���>|m;���%�ROo��6o�l��M4k~iMLm���^�O7#� vc��X��5+ׅ�Q�W�WԼU3�����?�R�v-e�Q�W��h����:�ނ�D�[c�^r�Dl0�=Z�HB���kG�i�����m��vh3C�w��׌}7�Adl]k�Y����4�f������DEk��u6>�c����U�7l�;|���9�r,�p��VK��n�MdW����ߍ�.�����M����`;Vd�>v�z��XWsg5���5��k�W�i��v�~�����.n���������ʹY�Y4떮�� 223 | iܥ4�t��6̰ZX;w6«�̾��Q�>�6���u����z|"���� ���w�up�UWL���ԭ���w���v7s�5��&����F�߉w��v���Q�i��9�ݨ� 1]���K6p��s����(��ڟ���E�e�ԛ ��`�<���]i�7�7�w�(ŽE�gy,Yٖh_�G���ԭ_[��Vn���S�[�˽ڔ5)9��Dei� ?�J^y�u�F�4Z�-X�����_���)2^�!!��� �,���7Y��Y��˵�AP��R�t�5_Β7o�K�L� M�jb�r�c/��G zZh�W_��{-K:i�Ih� �Y���������q�l�6���nj�W�Fhf�v����5s��^�E��� �͛~�͒�:Rg�9}�Ȧ�o���^ m>;�k�:G�?��O�\�?���1�.��Z�!��D��ʍ 224 | ��Y��M5��>�^��-c�%2�]���,Z^�8iSQ �,0�Ԭ{Yt��Ǿ7No�j '�z��D���6�K֟��k��Yn���ޜ"��|�� 225 | � �g�@D���%ut��,u8c���V"N�D[m��&��~������|����i�'YsQ �,�;V��n&=����?��Za}��WS��f�������Vi����I�$[`���צ��t(v��ak��7N��+�U�#��������?f��p+�m��n^sJ��g�sj��п��|U}����ڈ���)����<��?�r�>��/����D�� �YX^ێ5=��vl���R�^7-X ߟ�*Z��7߮�6�L�:�*l�jT��o�~m���N�����s��&���/��s,�n���@�l� ���K��3��[s�M���G�KdzNh�h}ҝ�||�ZmJڪ]��chۨ���QGC�Z�W[��d���Oh�����i�^�$�o\h���y�ˏ5� ���'��'Sۧ���q�+�}ڻ����d5�QIG_7KG-}Ԯ�ၠm��>��$u�'��(�޾-?����' pv ������џ��}3�}O�w>��-nߺC֯���Sx��.@�3F���i����� �@��D�E�~Z�]����H������l0�y��S�N@� ؿ}����{o�tX 226 |  ��I 227 | x�D O�� � � �@��`��0nA:ʊ � � �[�`�7@@@�� 228 |  ��Qr@@@����o:2"� � ��  � � � ��o�A�tdD@@�+@0�sG�@@@����Ȉ � �W�`0�玒#� � ��}ӑ@@��`p�%G@@|  ��## � � \����;J� � �� �MGF@@@ ���=w�@@�-@0蛎� � � �@p�{�(9 � � �[�`�7@@@�� 229 |  ��Qr@@@����o:2"� � ��  � � � ��o�A�tdD@@�+@0�sG�@@@����Ȉ � �W�`0�玒#� � ��}ӑ@@��`p�%G@@|  ��## � � \����;J� � �� �MGF@@@ ���=w�@@�-@0蛎� � � �@p�{�(9 � � �[�`�7@@@�� 230 |  ��Qr@@@����o:2"� � ��  � � � ��o�A�tdD@@�+@0�sG�@@@����Ȉ � �W�`0�玒#� � ��}ӑ@@��`p�%G@@|  ��## � � \����;J� � �� �MGF@@@ ���=w�@@�-@0蛎� � � �@p�{�(9 � � �[�`�7@@@�� 231 |  ��Qr@@@����o:2"� � ��  � � � ��o�A�tdD@@�+@0�sG�@@@����Ȉ � �W�`0�玒#� � ��}ӑ@@��`p�%G@@|  ��## � � \����;J� � �� �MGF@@@ ���=w�@@�-@0蛎� � � �@p�{�(9 � � �[�`�7@@@�� 232 |  ��Qr@@@����o:2"� � ��  � � � ��o�A�tdD@@�+@0�sG�@@@����Ȉ � �W�`0�玒#� � ��}ӑ@@��`p�%G@@|  ��## � � \����;J� � �� �MGF@@@ ���=w�@@�-@0蛎� � � �@p�{�(9 � � �[�`�7@@@�� 233 |  ��Qr@@@����o:2"� � ��  � � � ��o�A�tdD@@�+mE?x�D�$@@@�3[�DJ��`0?/��>Z�@@@�����V`��9:IEND�B`� --------------------------------------------------------------------------------