├── .ruby-version ├── .rspec ├── .gitignore ├── Procfile ├── config ├── deploy │ └── production.rb └── deploy.rb ├── docker-compose.yml ├── config.ru ├── Rakefile ├── bin ├── console └── deploy.sh ├── Gemfile ├── .github ├── dependabot.yml ├── workflows │ └── upgrade-ruby.yml ├── SUPPORT.md ├── PULL_REQUEST_TEMPLATE.md ├── ISSUE_TEMPLATE.md └── CONTRIBUTING.md ├── spec ├── app_spec.rb └── spec_helper.rb ├── Dockerfile ├── LICENSE.txt ├── Readme.md ├── app.rb ├── conda.rb ├── CODE_OF_CONDUCT.md ├── channel.rb └── Gemfile.lock /.ruby-version: -------------------------------------------------------------------------------- 1 | 3.4.4 2 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --require spec_helper 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | log 2 | coverage 3 | tmp 4 | *~ 5 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: puma config.ru --log-requests 2 | -------------------------------------------------------------------------------- /config/deploy/production.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | server "conda-api.libraries.io", user: "root", roles: %w[app web] 4 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | app: 4 | build: ./ 5 | ports: 6 | - 9292:9292 7 | environment: 8 | PORT: 9292 9 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "bundler" 4 | Bundler.require 5 | 6 | require File.expand_path "app.rb", __dir__ 7 | 8 | run CondaAPI 9 | -------------------------------------------------------------------------------- /config/deploy.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | lock "3.11.0" 4 | 5 | set :application, "conda_api" 6 | set :repo_url, "git@github.com:librariesio/conda-api.git" 7 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rspec/core' 2 | require 'rspec/core/rake_task' 3 | 4 | task :default => :spec 5 | 6 | desc "Run all specs in spec directory (excluding plugin specs)" 7 | RSpec::Core::RakeTask.new(:spec) -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require "bundler/setup" 5 | require_relative "../conda" 6 | 7 | # You can add fixtures and/or initialization code here to make experimenting 8 | # with your gem easier. You can also use a different console, if you like. -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | ruby '3.4.4' 5 | 6 | gem "builder" 7 | gem "concurrent-ruby" 8 | gem "foreman" 9 | gem "typhoeus" 10 | gem "puma" 11 | gem "rufus-scheduler" 12 | gem "sinatra" 13 | gem 'rake' 14 | 15 | group :test do 16 | gem "rack-test" 17 | gem "rspec" 18 | end 19 | 20 | gem "tzinfo-data", "~> 1.2020" -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "bundler" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | allow: 8 | - dependency-type: direct 9 | - dependency-type: indirect 10 | - package-ecosystem: "github-actions" 11 | directory: "/" 12 | schedule: 13 | interval: "daily" 14 | - package-ecosystem: "docker" 15 | directory: "/" 16 | schedule: 17 | interval: "daily" 18 | -------------------------------------------------------------------------------- /.github/workflows/upgrade-ruby.yml: -------------------------------------------------------------------------------- 1 | name: Upgrade Ruby 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: "0 0 * * 0" # Runs weekly 7 | 8 | permissions: 9 | contents: write 10 | pull-requests: write 11 | 12 | jobs: 13 | upgrade-ruby: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: andrew/ruby-upgrade-action@main 18 | with: 19 | github-token: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Libraries.io Support 2 | 3 | If you're looking for support for Libraries.io there are a lot of options, check out: 4 | 5 | * Documentation — https://docs.libraries.io 6 | * Email — support@libraries.io 7 | * Twitter — https://twitter.com/librariesio 8 | * Chat — https://slack.libraries.io 9 | 10 | On Discuss and in the Libraries.io Slack team, there are a bunch of helpful community members that should be willing to point you in the right direction. 11 | -------------------------------------------------------------------------------- /bin/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | REVISION=$(git show-ref origin/main |cut -f 1 -d ' ') 6 | TAGGED_IMAGE=gcr.io/${GOOGLE_PROJECT}/conda-api:${REVISION} 7 | gcloud --quiet container images describe ${TAGGED_IMAGE} || { status=$?; echo "Container not finished building" >&2; exit $status; } 8 | 9 | gcloud --quiet container images add-tag ${TAGGED_IMAGE} gcr.io/${GOOGLE_PROJECT}/conda-api:latest 10 | 11 | kubectl set image deployment/conda-service conda-api-container=${TAGGED_IMAGE} 12 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thanks taking the time to contribute. This template should help guide you through the process of creating a pull request for review. Please erase any part of this template that is not relevant to your pull request: 2 | 3 | 4 | - [ ] Have you followed the guidelines for [contributors](http://docs.libraries.io/contributorshandbook)? 5 | - [ ] Have you checked to ensure there aren't other open pull requests on the repository for a similar change? 6 | - [ ] Is there a corresponding ticket for your pull request? 7 | - [ ] Have you written new tests for your changes? 8 | - [ ] Have you successfully run the project with your changes locally? 9 | 10 | If so then please replace this section with a link to the ticket(s) it addressed, an explanation of your change and why you think we should include it. Thanks again! 11 | -------------------------------------------------------------------------------- /spec/app_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | describe CondaAPI do 4 | before do 5 | allow(Conda.instance).to receive(:reload_all) 6 | allow(Typhoeus).to receive(:get).and_return(json_load_fixture("pkgs/repodata.json")) 7 | end 8 | 9 | it "should show HelloWorld" do 10 | Conda.instance.reload_all 11 | get "/" 12 | expect(last_response).to be_ok 13 | end 14 | 15 | it "should get list of packages" do 16 | Conda.instance.reload_all 17 | get "/packages" 18 | expect(last_response).to be_ok 19 | json = JSON.parse(last_response.body) 20 | expect(json.keys.length).to eq 21022 21 | expect(json.keys[12]).to eq "absl-py" 22 | end 23 | 24 | it "should 404 on missing package" do 25 | Conda.instance.reload_all 26 | get "/package/something-fake" 27 | expect(last_response).to be_not_found 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:3.4.4-slim 2 | 3 | ENV APP_ROOT=/usr/src/app 4 | ENV DATABASE_PORT=5432 5 | WORKDIR $APP_ROOT 6 | 7 | # * Setup system 8 | # * Install Ruby dependencies 9 | RUN apt-get update && apt-get install -y --no-install-recommends \ 10 | build-essential \ 11 | git \ 12 | nodejs \ 13 | libpq-dev \ 14 | tzdata \ 15 | curl \ 16 | libyaml-dev \ 17 | libcurl4-openssl-dev \ 18 | && rm -rf /var/lib/apt/lists/* 19 | 20 | # Will invalidate cache as soon as the Gemfile changes 21 | COPY Gemfile Gemfile.lock .ruby-version $APP_ROOT/ 22 | 23 | RUN bundle config --global frozen 1 \ 24 | && bundle config set without 'test' \ 25 | && bundle install --jobs 2 26 | 27 | # ======================================================== 28 | # Application layer 29 | 30 | # Copy application code 31 | COPY . $APP_ROOT 32 | 33 | # Startup 34 | CMD ["bundle", "exec", "puma", "config.ru"] -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Copyright Tidelift, Inc. 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Conda API 2 | 3 | A small service to make it easier for [packages.ecosyste.ms](https://packages.ecosyste.ms/) to read data about Conda Packages in different channels. 4 | 5 | ## Essentials 6 | 7 | - Provide a REST interface for list of all names of packages (as json) 8 | - Provide a REST interface for list of versions for each package (as json) 9 | - Update info from Specs repo frequently 10 | 11 | ## Extras 12 | 13 | - Watch anaconda repos for updates 14 | - Tell Libraries about removed versions/packages 15 | 16 | ## Development 17 | 18 | ### Requirements 19 | * ruby 3.2.1 20 | * Installing via [RVM](http://rvm.io/) or [rbenv](https://github.com/rbenv/rbenv) is recommended 21 | * redis 22 | 23 | ### Local Development 24 | 25 | Run `bundle install` to download all dependencies. 26 | 27 | You can run a local server within a container with docker-compose `docker-compose up` or locally with `bundle exec puma`. 28 | 29 | The server should now be running port 9292. This can be verified by going to `http://localhost:9292` and verifying it sends back an 'Hello world' response. 30 | 31 | ### Tests 32 | 33 | Run the unit tests using `rspec` locally or within a built docker container `docker build -t ecosyste-ms/conda-api . && docker run -it -e PORT=9292 -p 9292:9292 ecosyste-ms/conda-api rspec`. 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thanks for taking the time to raise an issue. This template should guide you through the process of submitting a bug, enhancement or feature request. Please erase any part of this template that is not relevant to your issue. 2 | 3 | ## Bugs 4 | Before submitting a bug report: 5 | 6 | - [ ] Double-check that the bug is persistent, 7 | - [ ] Double-check the bug hasn't already been reported [on our issue tracker](https://github.com/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+org%3Alibrariesio), they *should* be labelled `bug` or `bugsnag`. 8 | 9 | If you have completed those steps then please replace this section with a description of the steps taken to recreate the bug, the expected behavior and the observed behavior. 10 | 11 | ## Enhancements and Features 12 | 13 | Before submitting an enhancement or feature request: 14 | 15 | - [ ] Check that the enhancement is not already [in our issue tracker](https://github.com/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+org%3Alibrariesio), they should be labelled 'enhancement'., 16 | - [ ] For large feature requests, check that your request aligns with our strategy http://docs.libraries.io/strategy. 17 | 18 | If you have complete the above step then please replace this section with a description of your proposed enhancement or feature, the motivation for it, an approach and any alternative approaches considered, and whether you are willing and able to create a pull request for it. Note that we may close this issue if it's not something we're planning on working on. -------------------------------------------------------------------------------- /app.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "sinatra/base" 4 | require_relative "conda" 5 | require_relative "app" 6 | require "rufus-scheduler" 7 | 8 | class CondaAPI < Sinatra::Base 9 | scheduler = Rufus::Scheduler.new 10 | 11 | set :host_authorization, { permitted_hosts: [] } 12 | 13 | configure :production, :development do 14 | enable :logging 15 | end 16 | 17 | get "/" do 18 | "Last updated at #{Conda.instance.channels.values.first.timestamp} \n" 19 | end 20 | 21 | get "/packages" do 22 | content_type :json 23 | Conda.instance.packages.to_json 24 | end 25 | 26 | get "/package/:name" do 27 | content_type :json 28 | Conda.instance.find_package(params["name"]).to_json 29 | end 30 | 31 | get "/package/:name/:version" do 32 | content_type :json 33 | package = Conda.instance.find_package(params["name"]).clone 34 | package[:versions] = package[:versions].select { |version| version[:number] == params["version"] } 35 | package.to_json 36 | end 37 | 38 | get "/:channel/" do 39 | content_type :json 40 | Conda.instance.packages_by_channel(params["channel"]).to_json 41 | end 42 | 43 | get "/:channel/:name" do 44 | content_type :json 45 | Conda.instance.package(params["channel"], params["name"]).to_json 46 | end 47 | 48 | get "/:channel/:name/:version" do 49 | content_type :json 50 | Conda.instance.package_by_channel(params["channel"], params["name"], params["version"]).to_json 51 | end 52 | 53 | scheduler.every "15m" do 54 | puts "Reloading packages..." 55 | Conda.instance.reload_all 56 | puts "Reload finished" 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /conda.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "./channel" 4 | require "singleton" 5 | 6 | class Conda 7 | include Singleton 8 | attr_reader :channels 9 | 10 | def initialize 11 | @channels = { 12 | "Main" => Channel.new("main", "repo.anaconda.com/pkgs"), 13 | "CondaForge" => Channel.new("conda-forge", "conda.anaconda.org"), 14 | } 15 | @lock = Concurrent::ReadWriteLock.new 16 | new_packages = all_packages 17 | 18 | @lock.with_write_lock { @packages = new_packages } 19 | end 20 | 21 | def packages_by_channel(channel) 22 | raise Sinatra::NotFound unless @channels.key?(channel) 23 | 24 | @channels[channel].packages 25 | end 26 | 27 | def package_by_channel(channel, name, version) 28 | raise Sinatra::NotFound unless @channels.key?(channel) 29 | 30 | @channels[channel].package_version(name, version) 31 | end 32 | 33 | def packages 34 | @lock.with_read_lock { @packages } 35 | end 36 | 37 | def all_packages 38 | global_packages = {} 39 | @channels.each_value do |channel| 40 | channel.only_one_version_packages.each do |package_name, package| 41 | if global_packages.key?(package_name) 42 | global_packages[package_name][:versions] += package[:versions].clone 43 | else 44 | global_packages[package_name] = package.clone 45 | end 46 | end 47 | end 48 | global_packages 49 | end 50 | 51 | def package(channel, name) 52 | packs = packages_by_channel(channel) 53 | raise Sinatra::NotFound unless packs.key?(name) 54 | 55 | packs[name] 56 | end 57 | 58 | def find_package(name) 59 | package = @channels.values.find { |channel| channel.packages.key?(name) }&.packages&.dig(name) 60 | raise Sinatra::NotFound if package.nil? 61 | 62 | package 63 | end 64 | 65 | def reload_all 66 | @channels.each_value(&:reload) 67 | new_packages = all_packages 68 | @lock.with_write_lock { @packages = new_packages } 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /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, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | 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 andrew@libraries.io. 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 [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /channel.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "typhoeus" 4 | require "json" 5 | require "benchmark" 6 | 7 | class Channel 8 | ARCHES = %w[linux-32 linux-64 linux-aarch64 linux-armv6l linux-armv7l linux-ppc64le osx-64 win-64 win-32 noarch zos-z].freeze 9 | 10 | attr_reader :timestamp 11 | 12 | def initialize(channel, domain) 13 | @channel_name = channel 14 | @domain = domain 15 | @timestamp = Time.now 16 | @lock = Concurrent::ReadWriteLock.new 17 | reload 18 | end 19 | 20 | def reload 21 | new_packages = retrieve_packages 22 | @lock.with_write_lock { @packages = new_packages } 23 | @timestamp = Time.now 24 | end 25 | 26 | def packages 27 | @lock.with_read_lock { @packages } 28 | end 29 | 30 | def package_version(name, version) 31 | @lock.with_read_lock do 32 | raise Sinatra::NotFound unless @packages.key?(name) 33 | 34 | @packages[name][:versions].filter { |package| package[:number] == version } 35 | end 36 | end 37 | 38 | def only_one_version_packages 39 | @lock.with_read_lock { remove_duplicate_versions(@packages) } 40 | end 41 | 42 | private 43 | 44 | def retrieve_packages 45 | packages = {} 46 | puts "Fetching packages for channel https://#{@domain}/#{@channel_name}..." 47 | channel_resp = Typhoeus.get("https://#{@domain}/#{@channel_name}/channeldata.json") 48 | channeldata = JSON.parse(channel_resp.body)["packages"] 49 | 50 | benchmark = Benchmark.measure do 51 | ARCHES.each do |arch| 52 | url = "https://#{@domain}/#{@channel_name}/#{arch}/repodata.json" 53 | puts "fetcing #{url}" 54 | resp = Typhoeus.get(url) 55 | blob = JSON.parse(resp.body)['packages'] 56 | blob.each_key do |key| 57 | version = blob[key] 58 | package_name = version["name"] 59 | 60 | unless packages.key?(package_name) 61 | package_data = channeldata[package_name] 62 | packages[package_name] = base_package(package_data, package_name) 63 | end 64 | 65 | packages[package_name][:versions] << release_version(key, version) 66 | end 67 | rescue => e 68 | puts "Failed to fetch for #{arch} https://#{@domain}/#{@channel_name}/#{arch}/repodata.json" 69 | end 70 | end 71 | puts "Finished in #{benchmark.real.round(1)} sec: #{packages.to_json.bytesize / 1_000_000}mb of data." 72 | packages 73 | end 74 | 75 | def base_package(package_data, package_name) 76 | { 77 | versions: [], 78 | repository_url: package_data["dev_url"], 79 | homepage: package_data["home"], 80 | licenses: package_data["license"], 81 | description: package_data["description"], 82 | name: package_name, 83 | } 84 | end 85 | 86 | def release_version(artifact, package_version) 87 | { 88 | artifact: artifact, 89 | download_url: "https://#{@domain}/#{@channel_name}/#{package_version['subdir']}/#{artifact}", 90 | number: package_version["version"], 91 | original_license: package_version["license"], 92 | published_at: package_version["timestamp"].nil? ? nil : Time.at(package_version["timestamp"] / 1000), 93 | dependencies: package_version["depends"], 94 | arch: package_version["subdir"], 95 | channel: @channel_name, 96 | } 97 | end 98 | 99 | def remove_duplicate_versions(packages) 100 | packages.each_value do |value| 101 | value[:versions] = value[:versions].uniq { |vers| vers[:number] } 102 | end 103 | packages 104 | end 105 | end 106 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "rack/test" 4 | require "rspec" 5 | 6 | require "./conda" 7 | require "./app" 8 | 9 | ENV["RACK_ENV"] = "test" 10 | 11 | module RSpecMixin 12 | include Rack::Test::Methods 13 | def app 14 | CondaAPI 15 | end 16 | end 17 | 18 | RSpec.configure do |config| 19 | config.include RSpecMixin 20 | # rspec-expectations config goes here. You can use an alternate 21 | # assertion/expectation library such as wrong or the stdlib/minitest 22 | # assertions if you prefer. 23 | config.expect_with :rspec do |expectations| 24 | # This option will default to `true` in RSpec 4. It makes the `description` 25 | # and `failure_message` of custom matchers include text for helper methods 26 | # defined using `chain`, e.g.: 27 | # be_bigger_than(2).and_smaller_than(4).description 28 | # # => "be bigger than 2 and smaller than 4" 29 | # ...rather than: 30 | # # => "be bigger than 2" 31 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 32 | end 33 | 34 | # rspec-mocks config goes here. You can use an alternate test double 35 | # library (such as bogus or mocha) by changing the `mock_with` option here. 36 | config.mock_with :rspec do |mocks| 37 | # Prevents you from mocking or stubbing a method that does not exist on 38 | # a real object. This is generally recommended, and will default to 39 | # `true` in RSpec 4. 40 | mocks.verify_partial_doubles = true 41 | end 42 | 43 | # This option will default to `:apply_to_host_groups` in RSpec 4 (and will 44 | # have no way to turn it off -- the option exists only for backwards 45 | # compatibility in RSpec 3). It causes shared context metadata to be 46 | # inherited by the metadata hash of host groups and examples, rather than 47 | # triggering implicit auto-inclusion in groups with matching metadata. 48 | config.shared_context_metadata_behavior = :apply_to_host_groups 49 | 50 | # The settings below are suggested to provide a good initial experience 51 | # with RSpec, but feel free to customize to your heart's content. 52 | # # This allows you to limit a spec run to individual examples or groups 53 | # # you care about by tagging them with `:focus` metadata. When nothing 54 | # # is tagged with `:focus`, all examples get run. RSpec also provides 55 | # # aliases for `it`, `describe`, and `context` that include `:focus` 56 | # # metadata: `fit`, `fdescribe` and `fcontext`, respectively. 57 | # config.filter_run_when_matching :focus 58 | # 59 | # # Allows RSpec to persist some state between runs in order to support 60 | # # the `--only-failures` and `--next-failure` CLI options. We recommend 61 | # # you configure your source control system to ignore this file. 62 | # config.example_status_persistence_file_path = "spec/examples.txt" 63 | # 64 | # # Limits the available syntax to the non-monkey patched syntax that is 65 | # # recommended. For more details, see: 66 | # # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ 67 | # # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ 68 | # # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode 69 | # config.disable_monkey_patching! 70 | # 71 | # # This setting enables warnings. It's recommended, but in some cases may 72 | # # be too noisy due to issues in dependencies. 73 | # config.warnings = true 74 | # 75 | # # Many RSpec users commonly either run the entire suite or an individual 76 | # # file, and it's useful to allow more verbose output when running an 77 | # # individual spec file. 78 | # if config.files_to_run.one? 79 | # # Use the documentation formatter for detailed output, 80 | # # unless a formatter has already been configured 81 | # # (e.g. via a command-line flag). 82 | # config.default_formatter = "doc" 83 | # end 84 | # 85 | # # Print the 10 slowest examples and example groups at the 86 | # # end of the spec run, to help surface which specs are running 87 | # # particularly slow. 88 | # config.profile_examples = 10 89 | # 90 | # # Run specs in random order to surface order dependencies. If you find an 91 | # # order dependency and want to debug it, you can fix the order by providing 92 | # # the seed, which is printed after each run. 93 | # # --seed 1234 94 | # config.order = :random 95 | # 96 | # # Seed global randomization in this process using the `--seed` CLI option. 97 | # # Setting this allows you to use `--seed` to deterministically reproduce 98 | # # test failures related to randomization by passing the same `--seed` value 99 | # # as the one that triggered the failure. 100 | # Kernel.srand config.seed 101 | end 102 | 103 | def fixture_path(name) 104 | "spec/fixtures/#{name}" 105 | end 106 | 107 | def load_fixture(name) 108 | File.open(fixture_path(name)).read 109 | end 110 | 111 | def json_load_fixture(name) 112 | JSON.parse(load_fixture(name)) 113 | end 114 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | base64 (0.3.0) 5 | builder (3.3.0) 6 | concurrent-ruby (1.3.5) 7 | diff-lcs (1.6.2) 8 | et-orbi (1.2.11) 9 | tzinfo 10 | ethon (0.16.0) 11 | ffi (>= 1.15.0) 12 | ffi (1.17.2) 13 | ffi (1.17.2-aarch64-linux-gnu) 14 | ffi (1.17.2-aarch64-linux-musl) 15 | ffi (1.17.2-arm-linux-gnu) 16 | ffi (1.17.2-arm-linux-musl) 17 | ffi (1.17.2-arm64-darwin) 18 | ffi (1.17.2-x86-linux-gnu) 19 | ffi (1.17.2-x86-linux-musl) 20 | ffi (1.17.2-x86_64-darwin) 21 | ffi (1.17.2-x86_64-linux-gnu) 22 | ffi (1.17.2-x86_64-linux-musl) 23 | foreman (0.88.1) 24 | fugit (1.11.1) 25 | et-orbi (~> 1, >= 1.2.11) 26 | raabro (~> 1.4) 27 | logger (1.7.0) 28 | mustermann (3.0.3) 29 | ruby2_keywords (~> 0.0.1) 30 | nio4r (2.7.4) 31 | puma (6.6.0) 32 | nio4r (~> 2.0) 33 | raabro (1.4.0) 34 | rack (3.1.15) 35 | rack-protection (4.1.1) 36 | base64 (>= 0.1.0) 37 | logger (>= 1.6.0) 38 | rack (>= 3.0.0, < 4) 39 | rack-session (2.1.1) 40 | base64 (>= 0.1.0) 41 | rack (>= 3.0.0) 42 | rack-test (2.2.0) 43 | rack (>= 1.3) 44 | rake (13.3.0) 45 | rspec (3.13.1) 46 | rspec-core (~> 3.13.0) 47 | rspec-expectations (~> 3.13.0) 48 | rspec-mocks (~> 3.13.0) 49 | rspec-core (3.13.4) 50 | rspec-support (~> 3.13.0) 51 | rspec-expectations (3.13.5) 52 | diff-lcs (>= 1.2.0, < 2.0) 53 | rspec-support (~> 3.13.0) 54 | rspec-mocks (3.13.5) 55 | diff-lcs (>= 1.2.0, < 2.0) 56 | rspec-support (~> 3.13.0) 57 | rspec-support (3.13.4) 58 | ruby2_keywords (0.0.5) 59 | rufus-scheduler (3.9.2) 60 | fugit (~> 1.1, >= 1.11.1) 61 | sinatra (4.1.1) 62 | logger (>= 1.6.0) 63 | mustermann (~> 3.0) 64 | rack (>= 3.0.0, < 4) 65 | rack-protection (= 4.1.1) 66 | rack-session (>= 2.0.0, < 3) 67 | tilt (~> 2.0) 68 | tilt (2.6.0) 69 | typhoeus (1.4.1) 70 | ethon (>= 0.9.0) 71 | tzinfo (2.0.6) 72 | concurrent-ruby (~> 1.0) 73 | tzinfo-data (1.2025.2) 74 | tzinfo (>= 1.0.0) 75 | 76 | PLATFORMS 77 | aarch64-linux-gnu 78 | aarch64-linux-musl 79 | arm-linux-gnu 80 | arm-linux-musl 81 | arm64-darwin 82 | ruby 83 | x86-linux-gnu 84 | x86-linux-musl 85 | x86_64-darwin 86 | x86_64-linux-gnu 87 | x86_64-linux-musl 88 | 89 | DEPENDENCIES 90 | builder 91 | concurrent-ruby 92 | foreman 93 | puma 94 | rack-test 95 | rake 96 | rspec 97 | rufus-scheduler 98 | sinatra 99 | typhoeus 100 | tzinfo-data (~> 1.2020) 101 | 102 | CHECKSUMS 103 | base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b 104 | builder (3.3.0) sha256=497918d2f9dca528fdca4b88d84e4ef4387256d984b8154e9d5d3fe5a9c8835f 105 | concurrent-ruby (1.3.5) sha256=813b3e37aca6df2a21a3b9f1d497f8cbab24a2b94cab325bffe65ee0f6cbebc6 106 | diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962 107 | et-orbi (1.2.11) sha256=d26e868cc21db88280a9ec1a50aa3da5d267eb9b2037ba7b831d6c2731f5df64 108 | ethon (0.16.0) sha256=bba0da1cea8ac3e1f5cdd7cb1cb5fc78d7ac562c33736f18f0c3eb2b63053d9e 109 | ffi (1.17.2) sha256=297235842e5947cc3036ebe64077584bff583cd7a4e94e9a02fdec399ef46da6 110 | ffi (1.17.2-aarch64-linux-gnu) sha256=c910bd3cae70b76690418cce4572b7f6c208d271f323d692a067d59116211a1a 111 | ffi (1.17.2-aarch64-linux-musl) sha256=69e6556b091d45df83e6c3b19d3c54177c206910965155a6ec98de5e893c7b7c 112 | ffi (1.17.2-arm-linux-gnu) sha256=d4a438f2b40224ae42ec72f293b3ebe0ba2159f7d1bd47f8417e6af2f68dbaa5 113 | ffi (1.17.2-arm-linux-musl) sha256=977dfb7f3a6381206dbda9bc441d9e1f9366bf189a634559c3b7c182c497aaa3 114 | ffi (1.17.2-arm64-darwin) sha256=54dd9789be1d30157782b8de42d8f887a3c3c345293b57ffb6b45b4d1165f813 115 | ffi (1.17.2-x86-linux-gnu) sha256=95d8f9ebea23c39888e2ab85a02c98f54acb2f4e79b829250d7267ce741dc7b0 116 | ffi (1.17.2-x86-linux-musl) sha256=41741449bab2b9530f42a47baa5c26263925306fad0ac2d60887f51af2e3b24c 117 | ffi (1.17.2-x86_64-darwin) sha256=981f2d4e32ea03712beb26e55e972797c2c5a7b0257955d8667ba58f2da6440e 118 | ffi (1.17.2-x86_64-linux-gnu) sha256=05d2026fc9dbb7cfd21a5934559f16293815b7ce0314846fee2ac8efbdb823ea 119 | ffi (1.17.2-x86_64-linux-musl) sha256=97c0eb3981414309285a64dc4d466bd149e981c279a56371ef811395d68cb95c 120 | foreman (0.88.1) sha256=59c022125b6a328a2ce63da8d70b731f5dd13519e8d9a4184c696538088ea00a 121 | fugit (1.11.1) sha256=e89485e7be22226d8e9c6da411664d0660284b4b1c08cacb540f505907869868 122 | logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203 123 | mustermann (3.0.3) sha256=d1f8e9ba2ddaed47150ddf81f6a7ea046826b64c672fbc92d83bce6b70657e88 124 | nio4r (2.7.4) sha256=d95dee68e0bb251b8ff90ac3423a511e3b784124e5db7ff5f4813a220ae73ca9 125 | puma (6.6.0) sha256=f25c06873eb3d5de5f0a4ebc783acc81a4ccfe580c760cfe323497798018ad87 126 | raabro (1.4.0) sha256=d4fa9ff5172391edb92b242eed8be802d1934b1464061ae5e70d80962c5da882 127 | rack (3.1.15) sha256=d12b3e9960d18a26ded961250f2c0e3b375b49ff40dbe6786e9c3b160cbffca4 128 | rack-protection (4.1.1) sha256=51a254a5d574a7f0ca4f0672025ce2a5ef7c8c3bd09c431349d683e825d7d16a 129 | rack-session (2.1.1) sha256=0b6dc07dea7e4b583f58a48e8b806d4c9f1c6c9214ebc202ec94562cbea2e4e9 130 | rack-test (2.2.0) sha256=005a36692c306ac0b4a9350355ee080fd09ddef1148a5f8b2ac636c720f5c463 131 | rake (13.3.0) sha256=96f5092d786ff412c62fde76f793cc0541bd84d2eb579caa529aa8a059934493 132 | rspec (3.13.1) sha256=b9f9a58fa915b8d94a1d6b3195fe6dd28c4c34836a6097015142c4a9ace72140 133 | rspec-core (3.13.4) sha256=f9da156b7b775c82610a7b580624df51a55102f8c8e4a103b98f5d7a9fa23958 134 | rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836 135 | rspec-mocks (3.13.5) sha256=e4338a6f285ada9fe56f5893f5457783af8194f5d08884d17a87321d5195ea81 136 | rspec-support (3.13.4) sha256=184b1814f6a968102b57df631892c7f1990a91c9a3b9e80ef892a0fc2a71a3f7 137 | ruby2_keywords (0.0.5) sha256=ffd13740c573b7301cf7a2e61fc857b2a8e3d3aff32545d6f8300d8bae10e3ef 138 | rufus-scheduler (3.9.2) sha256=55fa9e4db0ff69d7f38c804f17baba0c9bce5cba39984ae3c5cf6c039d1323b9 139 | sinatra (4.1.1) sha256=4e997b859aa1b5d2e624f85d5b0fd0f0b3abc0da44daa6cbdf10f7c0da9f4d00 140 | tilt (2.6.0) sha256=263d748466e0d83e510aa1a2e2281eff547937f0ef06be33d3632721e255f76b 141 | typhoeus (1.4.1) sha256=1c17db8364bd45ab302dc61e460173c3e69835896be88a3df07c206d5c55ef7c 142 | tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b 143 | tzinfo-data (1.2025.2) sha256=a92375a1fbb47d38fe88fd514c40a38cc8f97d168da2a6479f15185e86470939 144 | 145 | RUBY VERSION 146 | ruby 3.4.4p34 147 | 148 | BUNDLED WITH 149 | 2.6.9 150 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing to Libraries.io :heart: 2 | Thanks for considering contributing. These guidelines outline how to contribute to the [Libraries.io](http://github.com/librariesio) project. 3 | 4 | ## Table of Contents 5 | [What is Libraries.io all about?](#whats-librariesio-about) 6 | 7 | [Who is Libraries.io for?](#who-is-librariesio-for) 8 | 9 | [What should I know Before I get started?](#what-should-i-know-before-i-get-started) 10 | * [Code of conduct](#code-of-conduct) 11 | * [Language](#language) 12 | * [Installation and setup](#setup) 13 | 14 | [How can I contribute?](#how-can-i-contribute) 15 | * [Reporting bugs](#reporting-bugs) 16 | * [Suggesting enhancements](#suggesting-enhancements) 17 | * [Suggesting a new feature](#suggesting-new-features) 18 | * [Your first contribution](#your-first-contribution) 19 | * [Tackling something meatier](#tackling-something-meatier) 20 | 21 | [How can I talk to other contributors?](#how-can-i-talk-to-other-contributors) 22 | * [Chat](#chat) 23 | * [Video](#video) 24 | * [Social media](#twitter) 25 | 26 | [Who Are Libraries.io's Users?](#who-are-librariesios-users) 27 | 28 | [Our workflow](#workflow) 29 | 30 | 31 | ## What's Libraries.io About? 32 | _Our goal is to raise the quality of all software._ 33 | 34 | By outlining our [mission and strategy](/strategy.md) we hope to give you more power to make decisions and determine how best to spend your time. Specifically we tackle three distinct problems: 35 | 36 | * Discovery: _Helping developers make faster, more informed decisions about the software that they use._ 37 | * Maintainability: _Helping maintainers understand more about the software they depend upon and the consumers of their software._ 38 | * Sustainability: _Supporting undervalued software by highlighting shortfalls in contribution and funneling support to them._ 39 | 40 | The first of these problems is our foccus for Libraries.io. The other two we are trying to tackle at [Tidelift](https://tidelift.com). 41 | 42 | ## Who is Libraries.io For? 43 | Libraries.io currently caters for the needs of three distinct user groups: 44 | 45 | * Google: _is hungry for your linked datas so she can serve you up search traffic_ 46 | * Searcher: _is a developer with a problem, she is looking for something to help solve it._ 47 | * Maintainer: _has a project that is used within and/or incorporates open dependencies. She needs to ensure her project(s) are working as expected for users._ 48 | 49 | These groups have been expanded into [personas](/personas.md) for contributors to reference. 50 | 51 | ## What Should I Know Before I Get Started? 52 | 53 | ### Code of Conduct 54 | Libraries.io is an open and inclusive [community of people](https://github.com/orgs/librariesio/people) working together. We expect contributors to abide by our [contributor code of conduct](CODE_OF_CONDUCT.md) which basically say 'be excellent to each other'. Please report unacceptable behavior to conduct@libraries.io 55 | 56 | ### Language 57 | We communicate predominately in English. Contributions to the project should be made with English as the first language. We are happy for members of the community to communicate in a language other than English in chat, email and video but be aware that this might be considered exclusive by other members of the community. 58 | 59 | ### Documentation 60 | Documentation for the project as a whole is available at [docs.libraries.io](https://docs.libraries.io). These pages are generated from the [documentation](https://github.com/librariesio/documentation) repo. Documentation that needs to be in every repo is replicated in [required-files](https://github.com/librariesio/required-files) (currently limited to [GitHub templates](https://github.com/blog/2111-issue-and-pull-request-templates)). Otherwise documentation will be specific to that repo. For example the main [Libraries.io](https://github.com/librariesio/libraries.io) `README.md` contains information about installing and running the main rails application. 61 | 62 | ### Setup 63 | If you wish to make contributions to Libraries.io then you'll need a local version of the site to test. You can find instructions to install the correct Ruby version, Postgres, and to set up the database in our [README](https://github.com/librariesio/libraries.io/blob/master/README.md#getting-started). 64 | 65 | ## How Can I Contribute? 66 | 67 | ### Reporting Bugs 68 | 69 | The simplest thing that you can do to help us is by filing good bug reports, so here we go: 70 | 71 | #### Before Submitting a Bug Report 72 | 73 | * Double-check that the bug is persistent. The site is still in it's infancy and sometimes artifacts may appear and disappear. 74 | * Double-check the bug hasn't already been reported [on our issue tracker](https://github.com/search?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+org%3Alibrariesio), they *should* be labelled `bug` or `bugsnag`. 75 | 76 | If something hasn't been raised, you can go ahead and create a new issue using [the template](/issue_template.md). If you'd like to help investigate further or fix the bug just mention it in your issue and check out our [workflow](#workflow). 77 | 78 | ### Suggesting Enhancements 79 | 80 | The next simplest thing you can do to help us is by telling us how we can improve the features we already support, here we go: 81 | 82 | #### Before Submitting an Enhancement 83 | 84 | * Check that the enhancement is not already [in our issue tracker](https://github.com/search?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+org%3Alibrariesio), they should be labelled 'enhancement'. 85 | 86 | If there isn't already an issue for feature then go ahead and create a new issue for it using the [template](/issue_template.md). If you'd like to work on the enhancement then just mention it in a comment and check out our [workflow](#workflow). 87 | 88 | ### Suggesting New Features 89 | 90 | If you're into this zone then you need to understand a little more about what we're trying to achieve: 91 | 92 | #### Before Suggesting a Feature 93 | 94 | * Check that it aligns with [our strategy](strategy.md) and is specifically not in line with something we have said we will not do (for the moment this is anything to do with ranking *people*). 95 | * Check that the feature is not already [in our issue tracker](https://github.com/search?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+org%3Alibrariesio), they should be tagged 'feature'. 96 | 97 | If you're still thinking about that killer feature that no one else is thinking about then *please* create an issue for it using the [template](/issue_template.md). 98 | 99 | ### Your First Contribution 100 | You're in luck! We label issues that are ideal for first time contributors with [`first-pr`](https://github.com/search?l=&q=is%3Aopen+is%3Aissue+org%3Alibrariesio+label%3Afirst-pr&ref=advsearch&type=Issues&utf8=%E2%9C%93). For someone who wants something a little more meaty you might find an issue that needs some assistance with [`help wanted`](https://github.com/search?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+org%3Alibrariesio+label%3A%22help+wanted%22&type=Issues). Next you'll want to read our [workflow](#workflow). 101 | 102 | ### Tackling Something Meatier 103 | 104 | Tickets are labeled by size, skills required and to indicate workflow. Details can be found in our [labelling policy](/labelling.md). 105 | 106 | To get you started you might want to check out issues concerning [documentation](https://github.com/librariesio/documentation/issues/), [user experience](https://github.com/librariesio/libraries.io/labels/ux), [visual design](https://github.com/librariesio/libraries.io/issues/labels/visual%20design) or perhaps something already [awaiting help](https://github.com/librariesio/libraries.io/labels/help%20wanted). You may find the following useful: 107 | 108 | * Our [strategy](/strategy.md) which outlines what our goals are, how we are going to achieve those goals and what we are specifically going to avoid. 109 | * An [overview](/overview.md) of the components that make up the Libraries.io project and run the [https://libraries.io](https://libraries.io) site. 110 | 111 | ## How Can I Talk To Other Contributors? 112 | 113 | ### Chat 114 | We use [Slack](http://slack.io) for chat. There's an open invitation available to anyone who wishes to join the conversation at [http://slack.libraries.io](http://slack.libraries.io). 115 | 116 | We try to use the following channels accordingly: 117 | 118 | * `#general` channel is used for general, water cooler-type conversation, contributor updates and issue discussion. 119 | * `#events` is used to share and discuss events that may be of interest to or attended by members of the community 120 | * `#activity` contains notifications from the various platforms that we use to keep the Libraries.io project turning. Including notifications from GitHub, Twitter and our servers. 121 | 122 | Members are encouraged to openly discuss their work, their lives, share views and ask for help using chat. It should be considered a *safe space* in which there is *no such thing as a stupid question*. Conversely no one contributor should ever be expected to have read something said in a chat. If someone should know something then it should be written down as an issue and/or documented in an obvious place for others to find. 123 | 124 | ### Video 125 | [Google Hangouts](http://hangouts.google.com) is our preferred tool for video chat. We operate an [open hangout](http://bit.ly/2kWtYak) for anyone to jump into at any time to discuss issues face to face. 126 | 127 | ### Regular updates 128 | Contributors are encouraged to share what they're working on. We do this through daily or weekly updates in the `#general` channel on Slack. Updates should take the format 'currently working on X, expecting to move onto Y, blocked on Z' where x, y and z are issues in our [issue tracker](https://github.com/search?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+org%3Alibrariesio). 129 | 130 | Additionally we host an [open hangout](http://bit.ly/2kWtYak) for any contributor to join at *5pm BST/GMT on a Tuesday* to discuss their work, the next week's priorities and to ask questions of other contributors regarding any aspect of the project. Again this is considered a *safe space* in which *there is no such thing as a stupid question*. 131 | 132 | ### Mail 133 | The [core team](https://github.com/orgs/librariesio/teams/core) operate a mailing list for project updates. If you'd like to subscribe you'll find a form in the footer on [Libraries.io](http://libraries.io). 134 | 135 | ### Twitter 136 | We have an account on Twitter at [@librariesio](http://twitter.com/librariesio). This is predominately used to retweet news, events and musings by contributors rather than as a direct method of communication. Contributors are encouraged to use @librariesio in a tweet when talking about the project, so that we may retweet if appropriate. The account is moderated and protected by the [core team](https://github.com/orgs/librariesio/teams/core). 137 | 138 | ### Facebook 139 | We have a Facebook page at [@libraries.io](https://www.facebook.com/libraries.io). Again this is predominantly used to gather and reflect news, events and musings by contributors rather than as a direct method of communication. Contributors are encouraged to reference Libraries.io in a post when talking about the project, so that we may reflect this if appropriate. Again the account is moderated and protected by the [core team](https://github.com/orgs/librariesio/teams/core). 140 | 141 | ### Medium 142 | We have a Medium account at [@librariesio](https://medium.com/@librariesio) and once again it is used to reflect news, events and musings by contributors rather than a direct method of communication. Contributors are encouraged to reference @librariesio in a post when talking about the project, so that we may recommend it if appropriate. Again the account is moderated and protected by the [core team](https://github.com/orgs/librariesio/teams/core). 143 | 144 | ## Who Are Libraries.io's Users? 145 | Libraries.io focusses on the following personas: 146 | 147 | ### Google 148 | _Is hungry for linked data so she can serve you up search traffic_ 149 | 150 | ### 'Searcher' 151 | _Is a developer with a problem, she is looking for something to help solve it._ 152 | 153 | ### 'Extender' 154 | _Has her own ideas. She wants access to the raw data so that she can mash up her own service and offer it to the world._ 155 | 156 | ## Workflow 157 | In general we use [GitHub](https://help.github.com/) and [Git](https://git-scm.com/docs/gittutorial) to support our workflow. If you are unfamiliar with those tools then you should check them out until you feel you have a basic understanding of GitHub and a working understanding of Git. Specifically you should understand how forking, branching, committing, PRing and merging works. 158 | 159 | #### Forking 160 | We prefer that contributors fork the project in order to contribute. 161 | 162 | #### Branching 163 | We *try* to use principles of [GitHub-flow](https://lucamezzalira.com/2014/03/10/git-flow-vs-github-flow/) in our branching model. That is the `master` branch will always be deployable to the live site, and that every branch from that will be used to add a feature, fix a bug, improve something or otherwise represent an atomic unit of work. 164 | 165 | #### Ticketing 166 | We *try* to create an issue for everything. That is any bug, feature or enhancement that is worth an open, focussed and documented discussion. 167 | 168 | #### Labelling 169 | We constrain labels as they are a key part of our workflow. Tickets will be labeled according to our [labelling policy](/labelling.md). 170 | 171 | #### Templates 172 | We use templates to guide contributors toward good practice in [filing bugs, requesting enhancements and features](/issue_template.md) and in [issuing pull-requests](/pull_request_template.md). 173 | 174 | #### Commenting 175 | If it is possible to comment your contribution — for instance if you are contributing code — then do so in a way that is simple, clear, concise and lowers the level of understanding necessary for others to comprehend what comes afterward. If you are contributing code it is very likely it will be rejected if it does not contain sufficient comments. 176 | 177 | #### Committing 178 | When committing to a branch be sure to use plain, simple language that describes the incremental changes made on the branch toward the overall goal. Avoid unnecessary complexity. Simplify whenever possible. Assume a reasonable but not comprehensive knowledge of the tools, techniques and context of your work. 179 | 180 | #### Testing 181 | When adding or fixing functionality, tests should be added to help reduce future regressions and breakage. All tests are ran automatically when new commits are pushed to a branch. Pull requests with broken/missing tests are not likely to be merged. 182 | 183 | #### Submitting for Review 184 | Once a piece of work (in a branch) is complete it should be readied for review. This is your last chance to ensure that your contribution is [properly tested](#testing). If you are contributing code it is likely your contribution will be rejected if it would lower the test-coverage. Once this is done you can submit a pull-request following the [template](/pull_request_template.md). 185 | 186 | It is likely that your contributions will need to be checked by at least one member of the [core team](https://github.com/orgs/librariesio/teams/core) prior to merging. It is also incredibly likely that your contribution may need some re-work in order to be accepted. Particularly if it lacks an appropriate level of comments, tests or it is difficult to understand your commits. Please do not take offense if this is the case. We understand that contributors give their time because they want to improve the project but please understand it is another's responsibility to ensure that the project is maintainable, and good practices like these are key to ensuring that is possible. 187 | 188 | #### Reviewing a PR 189 | We appreciate that it may be difficult to offer constructive criticism, but it is a necessary part of ensuring the project is maintainable and successful. If it is difficult to understand something, request it is better documented and/or commented. If you do not feel assured of the robustness of a contribution, request it is better tested. If it is unclear what the goal of the piece of work is and how it relates to the [strategy](/strategy.md), request a clarification in the corresponding issue. If a pull-request has no corresponding issue, decreases test coverage or otherwise decreases the quality of the project. Reject it. Otherwise, merge it. 190 | 191 | #### Merging 192 | As we keep the `master` branch in a permanent state of 'deployment ready' once-merged your contribution will be live on the next deployment. 193 | 194 | #### Deploying 195 | Any member of the [deployers](https://github.com/orgs/librariesio/teams/deployers) team are able to redeploy the site. If you require a deployment then you might find one of them in our `#general` [chat channel on Slack](slack.libraries.io). 196 | --------------------------------------------------------------------------------