{"id":39136981,"url":"https://github.com/cloudboss/ofcourse","last_synced_at":"2026-01-17T21:26:26.043Z","repository":{"id":57491289,"uuid":"161117539","full_name":"cloudboss/ofcourse","owner":"cloudboss","description":"A Concourse resource generator","archived":false,"fork":false,"pushed_at":"2020-04-17T16:11:23.000Z","size":149,"stargazers_count":42,"open_issues_count":0,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-08-14T00:30:49.870Z","etag":null,"topics":["ci","cicd","concourse","concourse-ci","concourse-ci-resource","concourse-pipeline","concourse-resources","continuous-delivery","continuous-deployment","continuous-integration","go","golang"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cloudboss.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-12-10T04:38:29.000Z","updated_at":"2024-09-08T07:50:10.000Z","dependencies_parsed_at":"2022-08-29T20:31:36.351Z","dependency_job_id":null,"html_url":"https://github.com/cloudboss/ofcourse","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/cloudboss/ofcourse","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudboss%2Fofcourse","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudboss%2Fofcourse/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudboss%2Fofcourse/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudboss%2Fofcourse/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cloudboss","download_url":"https://codeload.github.com/cloudboss/ofcourse/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudboss%2Fofcourse/sbom","scorecard":{"id":292379,"data":{"date":"2025-08-11","repo":{"name":"github.com/cloudboss/ofcourse","commit":"7f73ea72e374296329964c74ac95b70a735c53fb"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":3,"reason":"Found 6/17 approved changesets -- score normalized to 3","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":0,"reason":"Project has not signed or included provenance with any releases.","details":["Warn: release artifact v0.2.2 not signed: https://api.github.com/repos/cloudboss/ofcourse/releases/25619351","Warn: release artifact v0.2.1 not signed: https://api.github.com/repos/cloudboss/ofcourse/releases/15412139","Warn: release artifact v0.2.0 not signed: https://api.github.com/repos/cloudboss/ofcourse/releases/14743098","Warn: release artifact v0.1.0 not signed: https://api.github.com/repos/cloudboss/ofcourse/releases/14674787","Warn: release artifact v0.2.2 does not have provenance: https://api.github.com/repos/cloudboss/ofcourse/releases/25619351","Warn: release artifact v0.2.1 does not have provenance: https://api.github.com/repos/cloudboss/ofcourse/releases/15412139","Warn: release artifact v0.2.0 does not have provenance: https://api.github.com/repos/cloudboss/ofcourse/releases/14743098","Warn: release artifact v0.1.0 does not have provenance: https://api.github.com/repos/cloudboss/ofcourse/releases/14674787"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Pinned-Dependencies","score":3,"reason":"dependency not pinned by hash detected -- score normalized to 3","details":["Warn: containerImage not pinned by hash: templates/Dockerfile:1","Warn: containerImage not pinned by hash: templates/Dockerfile:11: pin your Docker image by updating golang:1.12.7 to golang:1.12.7@sha256:f5486a917b57f8b14be4345604bc4654147416a327d6d63271a0c52c907001c4","Info:   0 out of   2 containerImage dependencies pinned","Info:   1 out of   1 goCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 22 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-17T18:36:11.327Z","repository_id":57491289,"created_at":"2025-08-17T18:36:11.328Z","updated_at":"2025-08-17T18:36:11.328Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28518625,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T18:55:29.170Z","status":"ssl_error","status_checked_at":"2026-01-17T18:55:03.375Z","response_time":85,"last_error":"SSL_read: 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":["ci","cicd","concourse","concourse-ci","concourse-ci-resource","concourse-pipeline","concourse-resources","continuous-delivery","continuous-deployment","continuous-integration","go","golang"],"created_at":"2026-01-17T21:26:25.444Z","updated_at":"2026-01-17T21:26:26.035Z","avatar_url":"https://github.com/cloudboss.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ofcourse\n\nThis is a library and a project skeleton generator for making your own [Concourse](https://concourse-ci.org/) resources in [Go](https://golang.org/), with an emphasis on testability.\n\nA Concourse resource consists of a Docker image containing three executables: `/opt/resource/check`, `/opt/resource/in`, and `/opt/resource/out`. When these are called by Concourse, they receive a JSON payload through standard input. They communicate back to Concourse by printing JSON back to standard output.\n\nThis library reduces the amount of boilerplate required by allowing you to implement just three methods `Check`, `In`, and `Out`. These methods receive all of their inputs via arguments, and send their output through normal return values. The library handles the serializing of JSON to and from standard output and input.\n\nThe project skeleton generator creates a simple resource that works out of the box. The project includes a Dockerfile, a Makefile, and a test suite with coverage for `Check`, `In`, and `Out`. There is also a sample pipeline that you can use to try out the resource immediately after running `make`, which builds the image and pushes it to your registry.\n\n# Generating a Project\n\nFirst read the [official documentation](https://concourse-ci.org/implementing-resource-types.html) on implementing your own resource.\n\nDownload and unzip the latest `ofcourse` release from [GitHub](https://github.com/cloudboss/ofcourse/releases).\n\nExample for Linux:\n\n```\n\u003e curl -L -O https://github.com/cloudboss/ofcourse/releases/download/v0.2.2/ofcourse_linux_amd64.zip\n\u003e unzip ofcourse_linux_amd64.zip\n```\n\nPut the `ofcourse` binary in a directory on your `PATH`, or run it directly from the current location with `./ofcourse`.\n\nThe `ofcourse` binary contains one subcommand called `init`, which takes three arguments:\n\n* `-R, --docker-registry` - This is the docker registry where you will push your image. This may be an image on docker hub such as `cloudboss/concourse-noop-resource` or a fully qualified name such as `artifactory.example.com/cloudboss/concourse-noop-resource`.\n\n* `-i, --import-path` - This is the Go import path for your project, often a GitHub repository like `github.com/cloudboss/concourse-noop-resource`.\n\n* `-r, --resource` - This is the name of the resource. A directory of this name will be created, and this name will be used to refer to your resource in the sample pipeline.\n\nRun `ofcourse init` to generate a project:\n\n```\n\u003e ./ofcourse init -R cloudboss/concourse-noop-resource -i github.com/cloudboss/concourse-noop-resource -r noop\n```\n\nChange to the directory that was created and build the docker image:\n\n```\n\u003e cd noop\n\u003e make\n```\n\nNow push the image to your registry. Note that you may need to [log in](https://docs.docker.com/engine/reference/commandline/login/) to the registry before running this.\n\n```\n\u003e make publish\n```\n\nNow that the image has been pushed to a registry, you can create the sample pipeline with the `fly` command:\n\n```\n\u003e fly -t mytarget set-pipeline -p noop -c pipeline.yml\n\u003e fly -t mytarget unpause-pipeline -p noop\n```\n\nSoon after creating the pipeline, the resource will be checked.\n\n![Check](images/resource-check.png)\n\nAfter the check produces a version, the `do-it` job will trigger.\n\n![Trigger](images/trigger.png)\n\nThe output of `get` is shown here, with colored log levels, and metadata displayed on the right.\n\n![Put](images/job-output.png)\n\nContinue development to make it do something useful, testing as you go.\n\n```\n\u003e make test\ngo test -v ./...\ngo: finding github.com/cloudboss/ofcourse/ofcourse latest\ngo: finding github.com/cloudboss/ofcourse latest\ngo: finding github.com/stretchr/testify/assert latest\n?   \tgithub.com/cloudboss/concourse-noop-resource/cmd/check\t[no test files]\n?   \tgithub.com/cloudboss/concourse-noop-resource/cmd/in\t[no test files]\n?   \tgithub.com/cloudboss/concourse-noop-resource/cmd/out\t[no test files]\n=== RUN   TestCheck\n--- PASS: TestCheck (0.00s)\n=== RUN   TestIn\n--- PASS: TestIn (0.00s)\n=== RUN   TestOut\n--- PASS: TestOut (0.00s)\nPASS\nok  \tgithub.com/cloudboss/concourse-noop-resource/resource\t0.003s\n```\n\n# Project Makefile\n\nThe project Makefile contains the following targets:\n\n`docker`: This does a `docker build`, and is the default `make` target.\n\n`publish`: This does a `docker push`.\n\n`test`: This runs the project's go tests.\n\n`fmt`: This runs `gofmt` on the project's go files.\n\nFor the `docker` and `publish` targets, if `VERSION` is passed to `make`, then the docker image will be tagged with that version.\n\n```\n\u003e make VERSION=1.2.3\n\u003e make publish VERSION=1.2.3\n```\n\n# Project README\n\nA skeleton `README.md` file is generated for the project, which should be filled in with valid descriptions, and should document the source config and parameters required to make the resource function properly. The format follows the ones used by builtin Concourse resources, for example the [git resource](https://github.com/concourse/git-resource) or the [s3 resource](https://github.com/concourse/s3-resource).\n\n# Logging\n\nSince Concourse resources communicate back to Concourse over standard output, they cannot print information to standard output. For this reason, each of the `Check`, `In`, and `Out` methods receive a `logger` argument that will print to standard error, using colored log levels. The log level may be configured in the `source` of every resource that uses the `ofcourse` library. The available levels are `debug`, `info`, `warn`, `error`, and `silent`.\n\n```\nresources:\n- name: noop\n  type: noop\n  check_every: 5m\n  source:\n    # Every resource created using ofcourse will have `log_level`\n    # as an available option in the source configuration. If not\n    # given, it defaults to `info`.\n    log_level: debug\n```\n\nThe logger has methods `Debugf`, `Infof`, `Warnf`, and `Errorf` for printing formatted strings to the Concourse UI.\n\nThe `silent` level is useful for unit tests, so that log output does not interfere with test output.\n\n```go\nvar (\n        testLogger = ofcourse.NewLogger(ofcourse.SilentLevel)\n)\n\n```\n\n# Environment\n\nConcourse passes [metadata](https://concourse-ci.org/implementing-resources.html#resource-metadata) about the build as environment variables to `in` and `out` commands. The `ofcourse` methods all receive an `environment` argument, which is a structure with `Get` and `GetAll` methods for retrieving the environment variables. This was done to make writing tests easier, so that fake environments can be passed in unit tests. The `check` command does not receive the Concourse metadata, however the `Check` method that uses this library still receives the environment argument for ease of testing in case it is useful. After all, there are other environment variables besides the ones passed explictly by Concourse.\n\nA new environment using the current environment's variables may be created with `NewEnvironment` without any arguments.\n\n```go\n\tenv := ofcourse.NewEnvironment()\n```\n\nThe variables may be explicitly passed in as well:\n\n```go\n\tvars := map[string]string{\n\t\t\"BUILD_ID\":            \"1\",\n\t\t\"BUILD_NAME\":          \"1\",\n\t\t\"BUILD_JOB_NAME\":      \"do-it\",\n\t\t\"BUILD_PIPELINE_NAME\": \"noop\",\n\t\t\"BUILD_TEAM_NAME\":     \"noop\",\n\t\t\"ATC_EXTERNAL_URL\":    \"https://concourse.example.com\",\n\t}\n\tenv := ofcourse.NewEnvironment(vars)\n\n\t// Tests here using the environment...\n```\n\nYou may call `env.Get` with or without a default value. The normal default is to return an empty string when the environment variable is not set.\n\n```go\n\tbuildName := env.Get(\"BUILD_NAME\")\n\tbuildJobName := env.Get(\"BUILD_JOB_NAME\", \"one-off\")\n```\n\nYou may call `env.GetAll` to get a `map[string]string` of the whole environment.\n\n```go\n\tvariables := env.GetAll()\n```\n\n# Source\n\nEvery Concourse resource, when defined in a pipeline, may set its configuration in a key called `source`, which has an implementation defined structure. `ofcourse` has a `Source` data type to represent this. Under the hood, it is a `map[string]interface{}`. This is passed to `Check`, `In`, and `Out` methods.\n\n# Params\n\nEvery `get` or `put` on a Concourse resource in a pipeline may define a `params` key. Like `source`, its structure is defined by the implementation. `ofcourse` defines this as `Params`, which is a `map[string]interface{}`. This is passed to `In` and `Out` methods.\n\n```\njobs:\n- name: do-it\n  plan:\n  - get: noop\n    trigger: true\n  - put: noop\n    params:\n      version_path: noop/version\n```\n\n# Version\n\nVersions in Concourse are arbitrary key/value pairs of strings. `ofcourse` represents this as a `Version`, which is a `map[string]string`. This is passed to `Check` and `In` methods.\n\n# Metadata\n\n`In` and `Out` methods may display metadata in the Concourse UI by returning `Metadata`. This is an array of `NameVal` structs, each of which has fields `Name` and `Value`. It must be returned from `In` and `Out` methods, but may be empty if not needed.\n\n```go\n\tmetadata := ofcourse.Metadata{\n\t\t{\n\t\t\tName: \"a\",\n\t\t\tValue: \"b\",\n\t\t},\n\t\t{\n\t\t\tName: \"c\",\n\t\t\tValue: \"d\",\n\t\t},\n\t}\n```\n\n# Check\n\n`Check` is called when Concourse does a resource check, or when a user runs `fly check-resource`. The method has the following signature:\n\n```go\nCheck(source Source,\n\tversion Version,\n\tenv Environment,\n\tlogger *Logger) ([]Version, error)\n```\n\n`Check` must return an array of `Version`s, which should be all versions since the `version` argument. When called the first time, the `version` argument will have a value of `nil`, and the returned versions array should contain just one item. An empty array may be returned if there is no version.\n\n# In\n\n`In` is called when a pipeline job does a `get` on the resource. The method has the following signature:\n\n```go\nIn(outputDirectory string,\n\tsource Source\n\tparams Params,\n\tversion Version,\n\tenv Environment,\n\tlogger *Logger) (Version, Metadata, error)\n```\n\nThe `outputDirectory` argument is where any artifacts retrieved from `Source` should be placed. `In` must return `Version` and `Metadata`, though both may be empty.\n\n# Out\n\n`Out` is called when a pipeline job does a `put` on the resource. The method has the following signature:\n\n```go\nOut(inputDirectory string,\n\tsource Source\n\tparams Params,\n\tenv Environment,\n\tlogger *Logger) (Version, Metadata, error)\n```\n\nThe `inputDirectory` argument is a directory containing subdirectories for all resources retrieved with `get` in a job, as well as all of the job's task outputs. The path to any specific files needed by `Out` should be defined in the `put` `params` in the pipeline, which will be available in the `Params` argument. `Out` must return `Version` and `Metadata`, though both may be empty.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcloudboss%2Fofcourse","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcloudboss%2Fofcourse","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcloudboss%2Fofcourse/lists"}