diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..625e337 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/trigger-fleet/.env +/upsert-fleet/.env +.idea \ No newline at end of file diff --git a/README.md b/README.md index ff6ce36..92acc67 100644 --- a/README.md +++ b/README.md @@ -1 +1,32 @@ -# shipyard-actions \ No newline at end of file +# Shipyard Actions + +This repository contains GitHub Actions designed to interact with Shipyard, allowing seamless integration with your CI/CD workflows. These actions enable you to dynamically trigger or update Shipyard fleets based on GitHub events like commits, pull requests, or merges. + +## Actions Included + +1. **Trigger Shipyard Fleet GitHub Action** - Trigger a Shipyard fleet using GitHub events. +2. **Upsert Fleet Action for Shipyard** - Update or create a fleet configuration in Shipyard from a YAML file in your repository. + +## Prerequisites + +Before you use these actions, you need: +- A Shipyard account. +- An API key from Shipyard for authentication. + +### Storing Your Shipyard API Key + +Your Shipyard API key should be stored as a secret in your GitHub repository: +1. Navigate to your repository on GitHub. +2. Go to 'Settings' > 'Secrets' and click on 'New repository secret'. +3. Name the secret `SHIPYARD_API_KEY` and paste your Shipyard API key as the value. +4. Click 'Add secret'. + +## Usage + +Each action has its detailed usage instructions in its respective directory. See the links below for specific guidance: +- [Trigger Shipyard Fleet Action](./trigger-fleet/README.md) +- [Upsert Fleet Action](./upsert-fleet/README.md) + +## Contributing + +Contributions are welcome! Please read our contributing guidelines for how to propose updates or improvements. diff --git a/trigger-fleet/Dockerfile b/trigger-fleet/Dockerfile new file mode 100644 index 0000000..9a04795 --- /dev/null +++ b/trigger-fleet/Dockerfile @@ -0,0 +1,8 @@ +FROM python:3.9-slim + +WORKDIR /app +COPY . . +RUN pip install --no-cache-dir shipyard-api==0.2.0 + +ENTRYPOINT ["python", "/app/trigger_fleet.py"] + diff --git a/trigger-fleet/README.md b/trigger-fleet/README.md new file mode 100644 index 0000000..ac62522 --- /dev/null +++ b/trigger-fleet/README.md @@ -0,0 +1,48 @@ +# Trigger Shipyard Fleet GitHub Action + +This action allows you to trigger a Shipyard fleet based on GitHub events such as commits, pull request openings, or merges. + +### Creating and Configuring the Workflow + +Create a YAML file in the `.github/workflows` directory of your repository with the following configuration: + +```yaml +name: Trigger Shipyard Fleet on Push +on: + push: + branches: + - main + +jobs: + trigger-fleet: + runs-on: ubuntu-latest + steps: + - name: Trigger Shipyard Fleet + uses: shipyardapp/shipyard-actions/trigger-fleet@v1 + with: + org_id: 'your-organization-id' + project_id: 'your-project-id' + fleet_id: 'your-fleet-id' + overrides: '{ + "vessel_overrides": [ + { + "name": "Execute Python Script", + "environment_variable_overrides": { + "name": "NEW VALUE" + } + } + ] +}' + shipyard_api_key: ${{ secrets.SHIPYARD_API_KEY }} + wait_for_run: 'false' + wait_time: 5 +``` +### Action Inputs: + +* org_id: Your organization ID in Shipyard. +* project_id: Your project ID in Shipyard. +* fleet_id: The ID of the fleet you want to trigger. +* overrides: JSON string of parameters to override the default fleet settings (optional). +* shipyard_api_key: Your Shipyard API key, stored securely in GitHub secrets. +* wait_for_run: Whether to wait for the fleet run to complete before the action finishes. **NOTE:** The success of the action will be determined by the final status of the fleet run. (default: false). +* wait_time: Time in seconds to wait before rechecking the fleet run status if wait_for_run is set to true (default: 5). \ No newline at end of file diff --git a/trigger-fleet/action.yaml b/trigger-fleet/action.yaml new file mode 100644 index 0000000..d4a8917 --- /dev/null +++ b/trigger-fleet/action.yaml @@ -0,0 +1,41 @@ +name: 'Trigger Shipyard Fleet' +description: 'Triggers a Shipyard fleet based on commits, PR opens, or merges in GitHub.' +inputs: + org_id: + description: 'The organization ID in Shipyard where the fleet is located.' + required: true + project_id: + description: 'The project ID within the organization in Shipyard.' + required: true + fleet_id: + description: 'The fleet ID that should be triggered.' + required: true + overrides: + description: 'JSON string of parameters to override the default fleet settings.' + required: false + default: '{}' + shipyard_api_key: + description: 'The API key to authenticate with Shipyard.' + required: true + wait_for_run: + description: 'Whether to wait for the fleet run to complete before finishing the action. This will return a failure if the fleet run Errors.' + required: false + default: 'false' + wait_time: + description: 'The amount of time in seconds to recheck the fleet run status if wait_for_run is set to true.' + required: false + default: '5' +outputs: + status: + description: 'The status of the fleet trigger operation.' +runs: + using: 'docker' + image: 'Dockerfile' + args: + - '--organization-id=${{ inputs.org_id }}' + - '--project-id=${{ inputs.project_id }}' + - '--fleet-id=${{ inputs.fleet_id }}' + - '--api-key=${{ inputs.shipyard_api_key }}' + - '--overrides=${{ inputs.overrides }}' + - '--wait-for-run=${{ inputs.wait_for_run }}' + - '--wait-time=${{ inputs.wait_time }}' diff --git a/trigger-fleet/trigger_fleet.py b/trigger-fleet/trigger_fleet.py new file mode 100644 index 0000000..49a3522 --- /dev/null +++ b/trigger-fleet/trigger_fleet.py @@ -0,0 +1,46 @@ +import time + +from shipyard_api import ShipyardClient + +COMPLETED_STATUSES = ["Success", "Errored", "Terminated"] + +import argparse + + +def get_args(): + parser = argparse.ArgumentParser() + parser.add_argument("--organization-id", dest="org_id", required=True) + parser.add_argument("--api-key", dest="api_key", required=True) + parser.add_argument("--project-id", dest="project_id", required=True) + parser.add_argument("--fleet-id", dest="fleet_id", required=True) + parser.add_argument("--overrides", dest="overrides", required=False) + parser.add_argument("--wait-time", dest="wait_time", required=False) + parser.add_argument("--wait-for-run", dest="wait_for_run", required=False) + return parser.parse_args() + + +def main(): + args = get_args() + + client = ShipyardClient(org_id=args.org_id, api_key=args.api_key, project_id=args.project_id) + + run_id = client.trigger_fleet(fleet_id=args.fleet_id, + fleet_overrides=args.overrides).get("data").get("fleet_run_id") + if args.wait_for_run: + + while True: + time.sleep(int(args.wait_time)) + status = client.get_run_status(fleet_id=args.fleet_id, run_id=run_id) + print(f"Run status: {status}") + + if status in COMPLETED_STATUSES: + print("Run completed") + if status != "Success": + exit(1) + break + + print("Waiting for run to complete...") + + +if __name__ == "__main__": + main() diff --git a/upsert-fleet/Dockerfile b/upsert-fleet/Dockerfile new file mode 100644 index 0000000..7714985 --- /dev/null +++ b/upsert-fleet/Dockerfile @@ -0,0 +1,9 @@ +FROM python:3.9-slim + +WORKDIR /app + +RUN pip install --no-cache-dir shipyard-api==0.2.0 + +COPY . . + +ENTRYPOINT ["python", "/app/upsert_fleet.py"] diff --git a/upsert-fleet/README.md b/upsert-fleet/README.md new file mode 100644 index 0000000..9d33d39 --- /dev/null +++ b/upsert-fleet/README.md @@ -0,0 +1,39 @@ +# Upsert Fleet Action for Shipyard + +This GitHub Action allows you to upsert a fleet configuration in Shipyard from a YAML file located in your GitHub repository. + +## Prerequisites + +- A Shipyard account with appropriate permissions. +- An API key from Shipyard. + +## Workflow Configuration + +Here is a sample workflow file to use the `upsert-fleet` action: + +```yaml +name: Upsert Shipyard Fleet +on: + push: + paths: + - 'path/to/your/yaml/**' +jobs: + upsert_fleet: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Upsert Fleet + uses: shipyardapp/shipyard-actions/upsert-fleet@v1 + with: + org_id: `your-org-id` + project_id: `your-project-id` + yaml_path: 'path/to/fleet-config.yaml' + shipyard_api_key: ${{ secrets.SHIPYARD_API_KEY }} +``` +### Action Inputs: +* org_id: Your organization ID in Shipyard. +* project_id: Your project ID in Shipyard. +* yaml_path: The path to the YAML file containing the fleet configuration. +* shipyard_api_key: Your Shipyard API key, stored securely in GitHub secrets. diff --git a/upsert-fleet/action.yaml b/upsert-fleet/action.yaml new file mode 100644 index 0000000..864f55d --- /dev/null +++ b/upsert-fleet/action.yaml @@ -0,0 +1,23 @@ +name: 'Upsert Fleet' +description: 'Upsert a fleet configuration in Shipyard using a YAML file.' +inputs: + org_id: + description: 'Organization ID' + required: true + project_id: + description: 'Project ID' + required: true + yaml_path: + description: 'Path to the YAML file containing fleet configuration' + required: true + shipyard_api_key: + description: 'The API key to authenticate with Shipyard.' + required: true +runs: + using: 'docker' + image: 'Dockerfile' + args: + - '--organization-id=${{ inputs.org_id }}' + - '--project-id=${{ inputs.project_id }}' + - '--yaml-path=${{ inputs.yaml_path }}' + - '--api-key=${{ inputs.shipyard_api_key }}' diff --git a/upsert-fleet/upsert_fleet.py b/upsert-fleet/upsert_fleet.py new file mode 100644 index 0000000..84ac072 --- /dev/null +++ b/upsert-fleet/upsert_fleet.py @@ -0,0 +1,35 @@ +import argparse +import sys + +import yaml +from shipyard_api import ShipyardClient + + +def get_args(): + parser = argparse.ArgumentParser() + parser.add_argument("--organization-id", dest="org_id", required=True) + parser.add_argument("--api-key", dest="api_key", required=True) + parser.add_argument("--project-id", dest="project_id", required=True) + parser.add_argument("--yaml-path", dest="yaml_path", required=True) + return parser.parse_args() + + +def main(): + try: + args = get_args() + client = ShipyardClient(org_id=args.org_id, api_key=args.api_key, project_id=args.project_id) + + with open(args.yaml_path, 'r') as file: + data = yaml.safe_load(file) + + client.upsert_fleet(data) + + except Exception as e: + print("An error occurred when attempting to upsert fleet. " + "Ensure the yaml is valid and the API key has the correct access.") + print(f"Error: {e}") + sys.exit(1) + + +if __name__ == "__main__": + main()