From 222f00b58748875927cc7218f61233844cc1a75b Mon Sep 17 00:00:00 2001 From: Zev Isert Date: Thu, 29 Feb 2024 14:23:25 -0800 Subject: [PATCH] Migrate to github actions (#312) * ci: switch to github actions * ci: move actions into workflows/ * ci: rename link checking ci job * docs: add a badge for gha link checker * ci: attempt to enable actions on PRs --- .github/workflows/link-checker.yaml | 72 ++++++++++++++++++++ .travis.yml | 13 ---- README.md | 100 ++++++++++++++++------------ link_checker/check.sh | 55 +++++++-------- 4 files changed, 160 insertions(+), 80 deletions(-) create mode 100644 .github/workflows/link-checker.yaml delete mode 100644 .travis.yml diff --git a/.github/workflows/link-checker.yaml b/.github/workflows/link-checker.yaml new file mode 100644 index 0000000..5d0251b --- /dev/null +++ b/.github/workflows/link-checker.yaml @@ -0,0 +1,72 @@ +name: Link Checking + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + check-links: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y curl bash ca-certificates + + - name: Find Comment + uses: peter-evans/find-comment@v3 + id: fc + if: github.event_name == 'pull_request' + with: + issue-number: ${{ github.event.pull_request.number }} + comment-author: "github-actions[bot]" + body-includes: + + - name: Check links + id: report + run: bash link_checker/check.sh README.md --markdown > report.md + + # An existing comment now has no issues + - name: Clear report + if: ${{ github.event_name == 'pull_request' && steps.fc.outputs.comment-id != '' }} + uses: peter-evans/create-or-update-comment@v4 + with: + comment-id: ${{ steps.fc.outputs.comment-id }} + edit-mode: replace + body: | + + Link checker still sees no broken links, all good! :tada: + + # No existing comment, and no issues found + - name: No issues + if: ${{ github.event_name == 'pull_request' && steps.fc.outputs.comment-id == '' }} + uses: peter-evans/create-or-update-comment@v4 + with: + issue-number: ${{ github.event.pull_request.number }} + body: | + + Link checker found no broken links! :sparkles: + + # No existing comment, but issues found + - name: Create new report + if: ${{ failure() && github.event_name == 'pull_request' && steps.report.conclusion == 'failure' && steps.fc.outputs.comment-id == '' }} + uses: peter-evans/create-or-update-comment@v4 + with: + issue-number: ${{ github.event.pull_request.number }} + body-path: "report.md" + + # Update existing comment with new report + - name: Update report + if: ${{ failure() && github.event_name == 'pull_request' && steps.report.conclusion == 'failure' && steps.fc.outputs.comment-id != '' }} + uses: peter-evans/create-or-update-comment@v4 + with: + comment-id: ${{ steps.fc.outputs.comment-id }} + edit-mode: replace + body-path: report.md diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 70101ba..0000000 --- a/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -language: generic - -dist: focal -addons: - apt: - update: true - packages: - - curl - - bash - - ca-certificates - -script: - - link_checker/check.sh README.md diff --git a/README.md b/README.md index 654ffd5..e311132 100755 --- a/README.md +++ b/README.md @@ -6,44 +6,53 @@ A well-maintained list of available startup jobs in Victoria BC. This list is cu [Tweet about us!][tweet-link] -[![Build Status](https://travis-ci.com/sendwithus/vic-startup-jobs.svg?branch=main)](https://app.travis-ci.com/sendwithus/vic-startup-jobs) +[![Link Checking](https://github.com/sendwithus/vic-startup-jobs/actions/workflows/link-checker.yaml/badge.svg?branch=main)](https://github.com/sendwithus/vic-startup-jobs/actions/workflows/link-checker.yaml) ### How to Apply? + Click the link for more info about each job posting, including how to apply. Most companies provide application instructions on their websites. ### How to Post? + Posting is easy and free, simply fork this repo and submit a pull request adding/updating your job posting. Avoid generic "call for resumes" and prefer specific job postings. If you have questions, need help, or want us to update the list for you, please email jobs@sendwithus.com. Note that there is a CI hook that runs after PR creation that checks for any 404s links on this page. You can run it manually prior to pushing: + ``` link_checker/check.sh README.md ``` + Running it before pushing up to Github allows you to remove broken job links from the list in addition to updating your own job postings. More info on the [associated README](/link_checker/README.md) ### Who can post? + Tech startups with offices in Victoria BC. If we notice that you haven't posted a role in ~6 months, we may remove your org from the list in order to keep it up-to-date. Email us when you have a job and we can re-add you! ### What about Co-Op & Intern Friendly Companies? -Companies that are known for hiring/placing co-op students and interns will have a `(👩‍💻 Co-Op / Intern Friendly)` following their name. These companies are always open to discussing opportunities for students and aspiring developers, designers, and marketers. + +Companies that are known for hiring/placing co-op students and interns will have a `(👩‍💻 Co-Op / Intern Friendly)` following their name. These companies are always open to discussing opportunities for students and aspiring developers, designers, and marketers. ### How can I get involved in the Victoria tech community? -* Connect with the Victoria tech community by joining the [YYJ Tech Slack group](https://yyj-tech.ca/) -* Connect with the Victoria ladies tech community by joining the [YYJ Tech Ladies Slack group](http://yyjtechladies.com/). Please note that this group is for anyone who is female. + +- Connect with the Victoria tech community by joining the [YYJ Tech Slack group](https://yyj-tech.ca/) +- Connect with the Victoria ladies tech community by joining the [YYJ Tech Ladies Slack group](http://yyjtechladies.com/). Please note that this group is for anyone who is female. ## Current Job Openings -Keep in mind that companies may have job postings on their own site that are not listed here - you should visit them and look for opportunities. Also, many companies are open to being contacted directly about opportunities, even if it's only to keep your resume on file. +Keep in mind that companies may have job postings on their own site that are not listed here - you should visit them and look for opportunities. Also, many companies are open to being contacted directly about opportunities, even if it's only to keep your resume on file. #### [Advanced Solutions](https://dxcas.com/index.php/careers/current-opportunities) -*[Chief Technology Officer](https://dxcas.com/index.php/careers/current-opportunities) -*[Director of Finance, Account Finance Lead](https://dxcas.com/index.php/careers/current-opportunities) -*[Senior Database Administrator (Oracle)](https://dxcas.com/index.php/careers/current-opportunities) -*[Open VMS Administrator](https://dxcas.com/index.php/careers/current-opportunities) + +_[Chief Technology Officer](https://dxcas.com/index.php/careers/current-opportunities) +_[Director of Finance, Account Finance Lead](https://dxcas.com/index.php/careers/current-opportunities) +_[Senior Database Administrator (Oracle)](https://dxcas.com/index.php/careers/current-opportunities) +_[Open VMS Administrator](https://dxcas.com/index.php/careers/current-opportunities) #### [AlayaCare](https://www.alayacare.com/) - + #### [Animikii](https://www.animikii.com/?utm_source=careers&utm_medium=website&utm_campaign=quote_contact&utm_content=logo) -* [Senior Full Stack Web Developer](https://animikii.applytojobs.ca/software/26682) + +- [Senior Full Stack Web Developer](https://animikii.applytojobs.ca/software/26682) #### [Audette](https://www.audette.io/) @@ -52,12 +61,14 @@ Keep in mind that companies may have job postings on their own site that are not #### [Bambora North America/Worldline](https://www.bambora.com/en/ca/) (👩‍💻 Co-Op / Intern Friendly) #### [Barnacle Systems](https://brnkl.io/) (👩‍💻 Co-Op / Intern Friendly) -* [Intermediate Front-End Developer](https://www.brnkl.io/careers) -* [Intermediate/Senior Full-Stack Developer](https://www.brnkl.io/careers) -* [IoT Systems & Mobile App Test Engineer - Co-op](https://www.brnkl.io/careers) -#### [Benevity](https://www.benevity.com/) (👩‍💻 Co-Op / Intern Friendly) -* [Director of Engineering & Analytics](https://benevity.com/job-postings?gh_jid=4945359004) +- [Intermediate Front-End Developer](https://www.brnkl.io/careers) +- [Intermediate/Senior Full-Stack Developer](https://www.brnkl.io/careers) +- [IoT Systems & Mobile App Test Engineer - Co-op](https://www.brnkl.io/careers) + +#### [Benevity](https://www.benevity.com/) (👩‍💻 Co-Op / Intern Friendly) + +- [Director of Engineering & Analytics](https://benevity.com/job-postings?gh_jid=4945359004) #### [Button](https://button.is/careers) @@ -102,39 +113,43 @@ Keep in mind that companies may have job postings on their own site that are not #### [KIXEYE Canada Ltd](https://www.kixeye.com/) #### [VertiGIS](https://vertigisstudio.com/) (👩‍💻 Co-Op / Intern Friendly) -* [Solutions Architect](https://vertigis.bamboohr.com/careers/122) + +- [Solutions Architect](https://vertigis.bamboohr.com/careers/122) #### [LlamaZOO](https://www.llamazoo.com/) (👩‍💻 Co-Op / Intern Friendly) #### [MetaLab](https://metalab.co/) (👩‍💻 Co-Op / Intern Friendly) -* [Principal Engineer, Machine Learning](https://www.metalab.com/careers#current-openings) -* [Design Technologist](https://www.metalab.com/careers#current-openings) -* [Senior Full Stack Developer, Machine Learning](https://www.metalab.com/careers#current-openings) -* [Principal Architect](https://www.metalab.com/careers#current-openings) -* [Director, Client Services](https://www.metalab.com/careers#current-openings) -* [Director, Finance (FP&A)](https://www.metalab.com/careers#current-openings) -* [Senior Full Stack Developer](https://www.metalab.com/careers#current-openings) -* [Principal Engineer](https://www.metalab.com/careers#current-openings) -* [Executive Assistant](https://www.metalab.com/careers#current-openings) -* [Partnerships Director](https://www.metalab.com/careers#current-openings) -* [Principal Visual Designer](https://www.metalab.com/careers#current-openings) -* [Brand Designer](https://www.metalab.com/careers#current-openings) -* [Design Lead](https://www.metalab.com/careers#current-openings) -* [Senior Design Director](https://www.metalab.com/careers#current-openings) + +- [Principal Engineer, Machine Learning](https://www.metalab.com/careers#current-openings) +- [Design Technologist](https://www.metalab.com/careers#current-openings) +- [Senior Full Stack Developer, Machine Learning](https://www.metalab.com/careers#current-openings) +- [Principal Architect](https://www.metalab.com/careers#current-openings) +- [Director, Client Services](https://www.metalab.com/careers#current-openings) +- [Director, Finance (FP&A)](https://www.metalab.com/careers#current-openings) +- [Senior Full Stack Developer](https://www.metalab.com/careers#current-openings) +- [Principal Engineer](https://www.metalab.com/careers#current-openings) +- [Executive Assistant](https://www.metalab.com/careers#current-openings) +- [Partnerships Director](https://www.metalab.com/careers#current-openings) +- [Principal Visual Designer](https://www.metalab.com/careers#current-openings) +- [Brand Designer](https://www.metalab.com/careers#current-openings) +- [Design Lead](https://www.metalab.com/careers#current-openings) +- [Senior Design Director](https://www.metalab.com/careers#current-openings) #### [Momentum Dashboard](https://momentumdash.com/) (👩‍💻 Co-Op / Intern Friendly) -* [Senior Front-End Developer](https://momentumdash.com/careers/#front-end-developer) -* [Full Stack Developer](https://momentumdash.com/careers/#product-designer) + +- [Senior Front-End Developer](https://momentumdash.com/careers/#front-end-developer) +- [Full Stack Developer](https://momentumdash.com/careers/#product-designer) #### [MonetizeMore](https://www.monetizemore.com/) #### [OneFeather](https://www.onefeather.ca/) #### [One Net Inc](https://www.onenetinc.com/crew#Careers) -* [Mobile Developer:Swift](https://www.onenetinc.com/careers/mobile-developer-swift) -* [Android Developer:Java](https://www.onenetinc.com/careers/mobile-developer-java) -* [Digital Media Buyer](https://www.onenetinc.com/careers/digital-media-buyer) -* [Account Strategist(Remote)](https://www.onenetinc.com/careers/account-strategist-remote) + +- [Mobile Developer:Swift](https://www.onenetinc.com/careers/mobile-developer-swift) +- [Android Developer:Java](https://www.onenetinc.com/careers/mobile-developer-java) +- [Digital Media Buyer](https://www.onenetinc.com/careers/digital-media-buyer) +- [Account Strategist(Remote)](https://www.onenetinc.com/careers/account-strategist-remote) #### [Othersphere](https://www.othersphere.io/) @@ -147,8 +162,9 @@ Keep in mind that companies may have job postings on their own site that are not #### [Redbrick](https://rdbrck.com/) (👩‍💻 Co-Op / Intern Friendly) #### [Regroove](https://regroove.ca) (👩‍💻 Co-Op / Intern Friendly) -* [Cloud Solutions Consultant](https://regroove.ca/careers/cloud-solutions-consultant/) -* [Project Manager](https://regroove.ca/careers/project-manager-regroove/) + +- [Cloud Solutions Consultant](https://regroove.ca/careers/cloud-solutions-consultant/) +- [Project Manager](https://regroove.ca/careers/project-manager-regroove/) #### [Revela Systems](https://www.revela.io/) @@ -163,8 +179,9 @@ Keep in mind that companies may have job postings on their own site that are not #### [STN Video (formerly SendtoNews)](https://www.stnvideo.com/careers/) #### [SilkStart](https://silkstart.com/about/) -* [Customer Success Specialist](https://www.silkstart.com/careers/) -* [Inside Sales](https://www.silkstart.com/careers/) + +- [Customer Success Specialist](https://www.silkstart.com/careers/) +- [Inside Sales](https://www.silkstart.com/careers/) #### [Starfish Medical](https://starfishmedical.com/) (👩‍💻 Co-Op / Intern Friendly) @@ -177,6 +194,7 @@ Keep in mind that companies may have job postings on their own site that are not #### [Vivid Solutions](https://www.vividsolutions.com/) #### [Workday](https://www.workday.com/en-us/company/careers/open-positions.html#?q=&location=BC,%20Canada) (👩‍💻 Co-Op / Intern Friendly) + --- ### Share this job board with others! diff --git a/link_checker/check.sh b/link_checker/check.sh index baf6911..139193a 100755 --- a/link_checker/check.sh +++ b/link_checker/check.sh @@ -48,26 +48,29 @@ find_links () { for line in "${linkmap[@]}"; do # For the lines in the mapfile, - # the keys are everything before a tab... - key="${line%$'\t*'}" - # ... and the values are everything after - value="${line##$'*\t'}" + # the keys and values are delimited by a tab. + IFS=$'\t' read -r key value <<< "$line" # Assign to the associative array. - # This whole loop body could just have been - # LINKS["${line%$'\t*'}"]="${line##$'*\t'}", - # but that's a little cryptic, no? LINKS[$key]=$value done [[ ! $QUIET ]] && echo >&2 "📊 Found ${#LINKS[@]} links in this document to test." - [[ $MARKDOWN ]] && echo "Checkboxes for links to remove: " + [[ $MARKDOWN ]] && echo -e "\n" _queue_link_tests exit_code=$? [[ ! $QUIET ]] && echo >&2 "✨ Done checking! [${exit_code} problem(s)]" + if [[ $MARKDOWN ]]; then + if [[ $exit_code -eq 0 ]]; then + echo -e "\nLink checker tested ${#LINKS[@]} links and found none to be invalid!" + else + echo -e "\nLink checker tested ${#LINKS[@]} links and found the above broken links. Could you please remove them?" + fi + fi + exit "$exit_code" } @@ -104,7 +107,7 @@ test_link () { return 0 # Skiping is OK, we warned elif [[ ! "${scheme}" =~ $http_or_https || -z "${scheme}" ]]; then - [[ $MARKDOWN ]] && echo "* [ ] --- ${text} ${link} (Missing or unknown scheme)" + [[ $MARKDOWN ]] && echo "- ${text} ${link} (Missing or unknown scheme)" [[ ! $QUIET ]] && echo >&2 "🦺 Skip: The scheme component on ${link} isn't http(s) or is empty." return 1 # Missing scheme, www.example.com will be linked to a file in the repo @@ -124,46 +127,46 @@ test_link () { # 300 (Multiple Choice) is an example, so let's report on anything >= 300. # Codes for reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status if [[ "$http_code" -ge 300 ]]; then - [[ $MARKDOWN ]] && echo "* [ ] ${http_code} ${text} ${link}" - [[ ! $QUIET ]] && echo >&2 "👻 [${http_code}] ${text} seems gone, from ${hostport}" + [[ $MARKDOWN ]] && echo -e "- ${text} (Page seems gone, status code ${http_code})\n > ${link}" + [[ ! $QUIET ]] && echo >&2 "👻 [${http_code}] REQUEST FAILED. ${link}" return 1 # HTTP error, this is what this whole script for here for fi # See https://curl.haxx.se/libcurl/c/libcurl-errors.html for more cases we might # want to handle. if [[ "$curl_status" -eq 6 ]]; then - [[ $MARKDOWN ]] && echo "* [ ] --- ${text} ${link} (UNRESOLVED HOST)" - [[ ! $QUIET ]] && echo >&2 "🌎 [---] DNS lookup didn't resolve ${hostport}." + [[ $MARKDOWN ]] && echo -e "- ${text} (DNS lookup didn't resolve ${hostport}.)\n > ${link}" + [[ ! $QUIET ]] && echo >&2 "🌎 [---] UNRESOLVED HOST. ${link}" return 1 # Failed DNS elif [[ "$curl_status" -eq 7 ]]; then - [[ $MARKDOWN ]] && echo "* [ ] --- ${text} ${link} (COULDNT'T CONNECT TO SERVER)" - [[ ! $QUIET ]] && echo >&2 "📡 [---] Couldn't connect to ${hostport}." + [[ $MARKDOWN ]] && echo -e "- ${text} (Couldn't connect to ${hostport})\n > ${link}" + [[ ! $QUIET ]] && echo >&2 "📡 [---] COULDNT'T CONNECT TO SERVER. ${link}" return 1 # Coudn't connect elif [[ "$curl_status" -eq 28 ]]; then - [[ $MARKDOWN ]] && echo "* [ ] --- ${text} ${link} (TIMEOUT WAS REACHED)" - [[ ! $QUIET ]] && echo >&2 "⌛ [---] Timed out with ${hostport}." + [[ $MARKDOWN ]] && echo -e "- ${text} (Timed out with ${hostport})\n > ${link}" + [[ ! $QUIET ]] && echo >&2 "⌛ [---] TIMEOUT WAS REACHED. ${link}" return 1 # Timed out elif [[ "$curl_status" -eq 35 ]]; then - [[ $MARKDOWN ]] && echo "* [ ] --- ${text} ${link} (FAILED SSL HANDSHAKE)" - [[ ! $QUIET ]] && echo >&2 "🤝 [---] Handshaking with ${hostport} failed." + [[ $MARKDOWN ]] && echo -e "- ${text} (TLS handshaking with ${hostport} failed)\n > ${link}" + [[ ! $QUIET ]] && echo >&2 "🤝 [---] FAILED TLS HANDSHAKE. ${link}" return 1 # Failed SSL elif [[ "$curl_status" -eq 60 || "$curl_status" -eq 51 ]]; then - [[ $MARKDOWN ]] && echo "* [ ] --- ${text} ${link} (FAILED SSL VERIFICATION)" - [[ ! $QUIET ]] && echo >&2 "🔓 [---] ${hostport}'s SSL certificate seems invalid!" + [[ $MARKDOWN ]] && echo -e "- ${text} (${hostport}'s TLS certificate seems invalid!)\n > ${link}" + [[ ! $QUIET ]] && echo >&2 "🔓 [---] FAILED TLS VERIFICATION. ${link}" return 1 # Failed SSL elif [[ "$curl_status" -eq 130 ]]; then - [[ $MARKDOWN ]] && echo "* [ ] --- ${text} ${link} (SIGINT)" - [[ ! $QUIET ]] && echo >&2 "🛑 [---] Interrupted checking ${hostport}!" + [[ $MARKDOWN ]] && echo -e "- ${text} (Interrupted checking ${hostport}!)\n > ${link}" + [[ ! $QUIET ]] && echo >&2 "🛑 [---] SIGINT. ${link}" return 1 # Interrupted elif [[ ! "$curl_status" -eq 0 ]]; then - [[ $MARKDOWN ]] && echo "* [ ] --- ${text} ${link} (CURLcode: ${curl_status})" - [[ ! $QUIET ]] && echo >&2 "🤷 [---] Checking ${hostport} failed with CURLcode ${curl_status}" + [[ $MARKDOWN ]] && echo -e "- ${text} (Checking ${hostport} failed with CURLcode ${curl_status})\n > ${link}" + [[ ! $QUIET ]] && echo >&2 "🤷 [---] CURLcode: ${curl_status}. ${link}" return 1 # cURL's unhappy, I'm unhappy fi @@ -286,7 +289,7 @@ _queue_link_tests () { # Argument defaults QUIET= MARKDOWN= -WORKERS=4 +WORKERS=10 INPUT_FILE=/dev/stdin # Argument parsing