{"id":34235125,"url":"https://github.com/muhammedikinci/pin","last_synced_at":"2026-03-13T15:30:37.073Z","repository":{"id":64304319,"uuid":"482458515","full_name":"muhammedikinci/pin","owner":"muhammedikinci","description":"pin - pipeline with Docker Golang API.","archived":false,"fork":false,"pushed_at":"2025-10-08T14:04:01.000Z","size":17989,"stargazers_count":16,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-19T07:59:08.785Z","etag":null,"topics":["actions","cli","cobra","command-line-tool","container","containerization","continuous-delivery","continuous-deployment","continuous-integration","docker","go","golang","pipeline","viper","workflow"],"latest_commit_sha":null,"homepage":"https://pinpipeline.com","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/muhammedikinci.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-04-17T07:38:05.000Z","updated_at":"2025-10-29T15:43:14.000Z","dependencies_parsed_at":"2024-06-20T06:58:42.238Z","dependency_job_id":"2cd03cb3-b92c-4d9e-8ca0-96234db043bd","html_url":"https://github.com/muhammedikinci/pin","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/muhammedikinci/pin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/muhammedikinci%2Fpin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/muhammedikinci%2Fpin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/muhammedikinci%2Fpin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/muhammedikinci%2Fpin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/muhammedikinci","download_url":"https://codeload.github.com/muhammedikinci/pin/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/muhammedikinci%2Fpin/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30469312,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-13T11:00:43.441Z","status":"ssl_error","status_checked_at":"2026-03-13T11:00:23.173Z","response_time":60,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["actions","cli","cobra","command-line-tool","container","containerization","continuous-delivery","continuous-deployment","continuous-integration","docker","go","golang","pipeline","viper","workflow"],"created_at":"2025-12-16T02:03:29.305Z","updated_at":"2026-03-13T15:30:37.061Z","avatar_url":"https://github.com/muhammedikinci.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cbr\u003e\n    \u003cimg src=\"asset/pin.png\" width=\"200\"/\u003e\n  \u003cbr\u003e\n\u003c/p\u003e\n\n# pin 🔥 [![pipeline](https://github.com/muhammedikinci/pin/actions/workflows/go.yml/badge.svg)](https://github.com/muhammedikinci/pin/actions/workflows/go.yml)\n\nLocal pipeline project with Docker Golang API. Run pipelines locally or as a daemon with real-time monitoring.\n\n![pingif](asset/pin.gif)\n\n\u003csup\u003e\u003csup\u003eterminal from [terminalgif.com](https://terminalgif.com)\u003c/sup\u003e\u003c/sup\u003e\n\n## 🚀 Daemon Mode \u0026 Real-time Monitoring\n\nPin can run as a long-running daemon service with SSE (Server-Sent Events) support for real-time pipeline monitoring and HTTP-triggered execution.\n\n### Key Features\n\n- **Long-running service**: Keep pin running as a daemon\n- **HTTP API**: Trigger pipelines via REST endpoints\n- **Real-time events**: Monitor pipeline execution via Server-Sent Events\n- **Remote monitoring**: Connect from multiple clients simultaneously\n- **Production ready**: Graceful shutdown and error handling\n\n### Architecture Overview\n\n![pingif](asset/pindaemon.svg)\n\n### Quick Start\n\n```bash\n# Start daemon mode\npin apply --daemon\n\n# Trigger pipeline from another terminal\ncurl -X POST -H \"Content-Type: application/yaml\" \\\n  --data-binary @pipeline.yaml \\\n  http://localhost:8081/trigger\n\n# Monitor real-time events\ncurl -N http://localhost:8081/events\n```\n\n### HTTP Endpoints\n\n| Endpoint   | Method | Description                                     |\n| ---------- | ------ | ----------------------------------------------- |\n| `/events`  | GET    | Server-Sent Events stream for real-time updates |\n| `/health`  | GET    | Health check and connected client count         |\n| `/trigger` | POST   | Trigger pipeline execution with YAML config     |\n| `/`        | GET    | API information and available endpoints         |\n\n### Real-time Events\n\nThe daemon broadcasts various events during pipeline execution:\n\n- **daemon_start**: Service started successfully\n- **pipeline_trigger**: New pipeline execution requested\n- **job_container_start**: Container started for job\n- **log**: Real-time log messages from jobs\n- **job_completed**: Job finished successfully\n- **job_failed**: Job failed with error details\n- **pipeline_complete**: Entire pipeline finished\n- **daemon_stop**: Service shutting down\n\n### Example Event Stream\n\n```javascript\n// Connect to event stream\nconst eventSource = new EventSource(\"http://localhost:8081/events\");\n\neventSource.onmessage = function (event) {\n  const data = JSON.parse(event.data);\n  console.log(`[${data.level}] ${data.message}`);\n};\n\n// Events received:\n// {\"level\":\"info\",\"message\":\"Pipeline execution started\",\"job\":\"build\"}\n// {\"level\":\"info\",\"message\":\"Container started\",\"job\":\"build\"}\n// {\"level\":\"success\",\"message\":\"Job completed successfully\",\"job\":\"build\"}\n```\n\n### Production Usage\n\n```bash\n# Run daemon with specific pipeline\npin apply --daemon -f production.yaml\n\n# Run daemon without initial pipeline (HTTP-only mode)\npin apply --daemon\n\n# Monitor from remote machine\ncurl -N http://your-server:8081/events\n\n# Trigger deployments via API\ncurl -X POST -H \"Content-Type: application/yaml\" \\\n  --data-binary @deployment.yaml \\\n  http://your-server:8081/trigger\n```\n\n# 🌐 Installation\n\n## Download latest release\n\nYou can download latest release from [here](https://github.com/muhammedikinci/pin/releases)\n\n## Install with cloning\n\nClone the pin\n\n```sh\ngit clone https://github.com/muhammedikinci/pin\n```\n\nDownload packages\n\n```sh\ngo mod download\n```\n\nBuild executable\n\n```sh\ngo build -o pin ./cmd/cli/.\n```\n\nOr you can run directly\n\n```sh\ngo run ./cmd/cli/. apply -n \"test\" -f ./testdata/test.yaml\n```\n\n# ⚙️ Configuration\n\nPin includes built-in YAML validation to catch configuration errors before pipeline execution.\n\n## Pipeline Validation\n\nPin automatically validates your pipeline configuration before execution:\n\n- ✅ **Required fields**: Ensures either `image` or `dockerfile` is specified\n- ✅ **Field types**: Validates all fields have correct data types\n- ✅ **Port formats**: Checks port configurations match supported formats\n- ✅ **Script validation**: Ensures scripts are not empty\n- ✅ **Boolean fields**: Validates boolean configurations\n\n### Validation Examples\n\n```bash\n# Valid configuration passes validation\n$ pin apply -f pipeline.yaml\nPipeline validation successful\n⚉ build Starting...\n\n# Invalid configuration shows helpful errors\n$ pin apply -f invalid.yaml\nPipeline validation failed: validation error in job 'build': either 'image' or 'dockerfile' must be specified\n```\n\n## Sample yaml file\n\n```yaml\nworkflow:\n  - run\n\nlogsWithTime: true\n\n# Optional: Specify custom Docker host\ndocker:\n  host: \"tcp://localhost:2375\"\n\nrun:\n  image: golang:alpine3.15\n  copyFiles: true\n  soloExecution: true\n  script:\n    - go mod download\n    - go run .\n    - ls\n  port:\n    - 8082:8080\n```\n\nYou can create separate jobs like the `run` stage and if you want to run these jobs in the pipeline you must add its name to `workflow`.\n\n## docker\n\nConfigure Docker daemon connection settings.\n\n### host\n\ndefault: system default (usually `unix:///var/run/docker.sock` on Linux/macOS)\n\nSpecify a custom Docker host to connect to a different Docker daemon. This is useful for:\n\n- **Remote Docker**: Connect to Docker running on another machine\n- **Docker Desktop**: Connect to Docker Desktop on different ports\n- **CI/CD environments**: Connect to specific Docker instances\n- **Development**: Switch between local and remote Docker instances\n\n### Supported Docker Host Formats\n\n```yaml\n# TCP connection to remote Docker daemon\ndocker:\n  host: \"tcp://192.168.1.100:2375\"\n\n# TCP connection with TLS (secure)\ndocker:\n  host: \"tcp://docker.example.com:2376\"\n\n# Unix socket (Linux/macOS default)\ndocker:\n  host: \"unix:///var/run/docker.sock\"\n\n# Windows named pipe\ndocker:\n  host: \"npipe://./pipe/docker_engine\"\n\n# SSH connection to remote host\ndocker:\n  host: \"ssh://user@docker-host\"\n```\n\n### Examples\n\n```yaml\n# Connect to local Docker Desktop\nworkflow:\n  - build\n\ndocker:\n  host: \"tcp://localhost:2375\"\n\nbuild:\n  image: golang:alpine\n  script:\n    - go build .\n\n# Connect to remote Docker daemon\nworkflow:\n  - deploy\n\ndocker:\n  host: \"tcp://production-docker:2375\"\n\ndeploy:\n  image: alpine:latest\n  script:\n    - echo \"Deploying to remote Docker\"\n```\n\n### Security Notes\n\n- Use TLS (port 2376) for remote connections in production\n- Ensure Docker daemon is properly secured when exposing TCP ports\n- Consider using SSH tunneling for secure remote connections\n\n## copyFiles\n\ndefault: false\n\nIf you want to copy all projects filed to the docker container, you must set this configuration to `true`\n\n## soloExecution\n\ndefault: false\n\nWhen you add multiple commands to the `script` field, commands are running in the container as a shell script. If soloExecution is set to `true` each command works in a different shell script.\n\n#### soloExecution =\u003e false\n\n```sh\n# shell#1\ncd cmd\nls\n```\n\n#### soloExecution =\u003e true\n\n```sh\n# shell#1\ncd cmd\n```\n\n```sh\n# shell#2\nls\n```\n\nIf you want to see all files in the cmd folder you must set soloExecution to false or you can use this:\n\n```sh\n# shell#1\ncd cmd \u0026\u0026 ls\n```\n\n## logsWithTime\n\ndefault: false\n\nlogsWithTime =\u003e true\n\n```sh\n⚉ 2022/05/08 11:36:30 Image is available\n⚉ 2022/05/08 11:36:30 Start creating container\n⚉ 2022/05/08 11:36:33 Starting the container\n⚉ 2022/05/08 11:36:35 Execute command: ls -a\n```\n\nlogsWithTime =\u003e false\n\n```sh\n⚉ Image is available\n⚉ Start creating container\n⚉ Starting the container\n⚉ Execute command: ls -a\n```\n\n## port\n\ndefault: empty mapping\n\nYou can use this feature for port forwarding from container to your machine with flexible host and port configuration.\n\n### Port Configuration Formats\n\n1. **Standard format**: `\"hostPort:containerPort\"`\n2. **Custom host format**: `\"hostIP:hostPort:containerPort\"`\n\n### Examples\n\n```yaml\n# Standard port mapping (binds to all interfaces)\nport: \"8080:80\"\n\n# Multiple ports with different configurations\nport:\n  - \"8082:8080\"                    # Standard format\n  - \"127.0.0.1:8083:8080\"          # Bind only to localhost\n  - \"192.168.1.100:8084:8080\"      # Bind to specific IP address\n\n# Mix of standard and custom host formats\nrun:\n  image: nginx:alpine\n  port:\n    - \"8080:80\"                    # Available on all network interfaces\n    - \"127.0.0.1:8081:80\"          # Only accessible from localhost\n    - \"0.0.0.0:8082:80\"            # Explicitly bind to all interfaces\n```\n\n### Use Cases\n\n- **Security**: Bind services only to localhost (`127.0.0.1:8080:80`)\n- **Network isolation**: Bind to specific network interfaces (`192.168.1.100:8080:80`)\n- **Development**: Expose different ports for different environments\n\n## copyIgnore\n\ndefault: empty mapping\n\nYou can use this feature to ignore copying the specific files in your project to the container.\n\nSample configuration yaml\n\n```yaml\nrun:\n  image: node:current-alpine3.15\n  copyFiles: true\n  soloExecution: true\n  port:\n    - 8080:8080\n  copyIgnore:\n    - server.js\n    - props\n    - README.md\n    - helper/.*/.py\n```\n\nActual folder structure in project\n\n```yaml\nindex.js\nserver.js\nREADME.md\nhelper:\n    - test.py\n    - mock\n        test2.py\n    - api:\n        index.js\n    - props:\n        index.js\n```\n\nFolder structure in container\n\n```yaml\nindex.js\nhelper:\n    - mock (empty)\n    - api:\n        index.js\n```\n\n## parallel\n\ndefault: false\n\nIf you want to run parallel job, you must add `parallel` field and the stage must be in workflow(position doesn't matter)\n\n```yaml\nworkflow:\n  - testStage\n  - parallelJob\n  - run\n---\nparallelJob:\n  image: node:current-alpine3.15\n  copyFiles: true\n  soloExecution: true\n  parallel: true\n  script:\n    - ls -a\n```\n\n## Environment Variables\n\nYou can specify environment variables for your jobs in the YAML configuration. These variables will be available inside the container during job execution.\n\nExample:\n\n```yaml\nworkflow:\n  - run\n\nrun:\n  image: golang:alpine3.15\n  copyFiles: true\n  soloExecution: true\n  script:\n    - go mod download\n    - go run .\n    - echo \"Environment variables:\"\n    - echo \"MY_VAR: $MY_VAR\"\n    - echo \"ANOTHER_VAR: $ANOTHER_VAR\"\n  port:\n    - 8082:8080\n  env:\n    - MY_VAR=value\n    - ANOTHER_VAR=another_value\n```\n\nIn this example, the environment variables `MY_VAR` and `ANOTHER_VAR` are set and printed during job execution.\n\n## Job Retry Mechanism\n\nPin supports automatic job retries with configurable parameters for handling transient failures.\n\n### retry\n\ndefault: no retry (attempts: 1)\n\nConfigure automatic retry behavior for jobs that fail due to temporary issues like network problems, resource constraints, or external service unavailability.\n\n### Retry Configuration Options\n\n```yaml\nretry:\n  attempts: 3 # Number of attempts (1-10, default: 1)\n  delay: 5 # Initial delay in seconds (0-300, default: 1)\n  backoff: 2.0 # Exponential backoff multiplier (0.1-10.0, default: 1.0)\n```\n\n### Examples\n\n```yaml\n# Simple retry - 3 attempts with 2 second delays\nworkflow:\n  - unstable-service\n\nunstable-service:\n  image: alpine:latest\n  retry:\n    attempts: 3\n    delay: 2\n  script:\n    - echo \"Attempting to connect to service...\"\n    - curl https://unstable-api.example.com/health\n\n# Advanced retry with exponential backoff\nworkflow:\n  - network-dependent\n\nnetwork-dependent:\n  image: alpine:latest\n  retry:\n    attempts: 5      # Try 5 times total\n    delay: 1         # Start with 1 second delay\n    backoff: 2.0     # Double delay each retry (1s, 2s, 4s, 8s)\n  script:\n    - wget https://external-resource.com/data.zip\n```\n\n### Retry Behavior\n\n1. **Linear Delays**: With `backoff: 1.0`, delays remain constant\n2. **Exponential Backoff**: With `backoff \u003e 1.0`, delays increase exponentially\n3. **Failure Logging**: Each retry attempt is logged with reason and next attempt time\n4. **Final Failure**: After all attempts fail, the job fails with the last error\n\n### Use Cases\n\n- **Network Operations**: Downloads, API calls, external service connections\n- **Resource Competition**: Database connections, file locks, temporary resource unavailability\n- **CI/CD Pipelines**: Flaky tests, temporary infrastructure issues\n- **External Dependencies**: Third-party services, cloud resources\n\n## Conditional Execution\n\nYou can specify conditions for job execution using the `condition` field. Jobs will only run if the condition evaluates to true.\n\nExample:\n\n```yaml\nworkflow:\n  - build\n  - test\n  - deploy\n\nbuild:\n  image: golang:alpine3.15\n  copyFiles: true\n  script:\n    - go build -o app .\n\ntest:\n  image: golang:alpine3.15\n  copyFiles: true\n  script:\n    - go test ./...\n\ndeploy:\n  image: alpine:latest\n  condition: $BRANCH == \"main\"\n  script:\n    - echo \"Deploying to production...\"\n    - ./deploy.sh\n```\n\n### Supported Condition Operators\n\n- **Equality**: `$VAR == \"value\"` - Check if variable equals value\n- **Inequality**: `$VAR != \"value\"` - Check if variable does not equal value\n- **AND**: `$VAR1 == \"value1\" \u0026\u0026 $VAR2 == \"value2\"` - Both conditions must be true\n- **OR**: `$VAR1 == \"value1\" || $VAR2 == \"value2\"` - At least one condition must be true\n- **Variable existence**: `$VAR` - Check if variable exists and is not empty/false/0\n\n### Examples\n\n```yaml\n# Run only on main branch\ndeploy:\n  condition: $BRANCH == \"main\"\n\n# Run on main or develop branch\ndeploy:\n  condition: $BRANCH == \"main\" || $BRANCH == \"develop\"\n\n# Run only when both conditions are met\ndeploy:\n  condition: $BRANCH == \"main\" \u0026\u0026 $DEPLOY == \"true\"\n\n# Run when variable exists\ncleanup:\n  condition: $CLEANUP_ENABLED\n\n# Run when environment is not test\ndeploy:\n  condition: $ENV != \"test\"\n```\n\nYou can set environment variables before running pin:\n\n```bash\nBRANCH=main pin apply -f pipeline.yaml\n```\n\n## Custom Dockerfile\n\nYou can use a custom Dockerfile to build your own image for the job instead of pulling a pre-built image.\n\nExample:\n\n```yaml\nworkflow:\n  - custom-build\n\ncustom-build:\n  dockerfile: \"./Dockerfile\"\n  copyFiles: true\n  script:\n    - echo \"Hello from custom Docker image!\"\n    - ls -la\n```\n\n### Key Features\n\n- **dockerfile**: Path to your custom Dockerfile\n- **Automatic image building**: Pin will build the image from your Dockerfile before running the job\n- **Build context**: The directory containing the Dockerfile will be used as the build context\n- **Image naming**: Built images are automatically tagged as `\u003cjob-name\u003e-custom:latest`\n\n### Example Dockerfile\n\n```dockerfile\nFROM alpine:latest\n\nRUN apk add --no-cache \\\n    bash \\\n    curl \\\n    git \\\n    make\n\nWORKDIR /app\nUSER nobody\n\nCMD [\"/bin/bash\"]\n```\n\n**Note**: When using `dockerfile`, you don't need to specify the `image` field. Pin will use the built image automatically.\n\n# Tests\n\n```sh\ngo test ./...\n```\n\n## 📚 Documentation\n\nFor comprehensive documentation, examples, and guides:\n\n- **[📖 Complete Documentation](docs/README.md)** - Full documentation index\n- **[🚀 Examples](docs/examples.md)** - Practical examples and use cases\n- **[🌐 API Reference](docs/api-reference.md)** - HTTP API documentation for daemon mode\n- **[🔧 Troubleshooting](docs/troubleshooting.md)** - Common issues and solutions\n- **[🎯 Use Cases](docs/use-cases.md)** - Real-world applications and workflows\n\n## 🤝 Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## 📞 Support\n\n- [GitHub Issues](https://github.com/muhammedikinci/pin/issues) - Bug reports and feature requests\n- [GitHub Discussions](https://github.com/muhammedikinci/pin/discussions) - Community discussions\n\n# Contact\n\nMuhammed İkinci - muhammedikinci@outlook.com\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmuhammedikinci%2Fpin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmuhammedikinci%2Fpin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmuhammedikinci%2Fpin/lists"}