Make Your Github Profile Dynamic

Yes, You read it right. You don’t have to do anything and your profile will be updated with dynamic contents.
Let me first give you an overview of Github hidden repository. Earlier we used to create github pages to create own portfolio but recently github has revealed a secret where you can create a repo of your username and it will be shown as home page whenever someone visits your profile.
Bit complicated? Look at the below image

If you remember, there was just a section of pinned repo and contributions, but now with this change people can see your portfolio as well.
For every github user, there is hidden repo, you just have to create a repository and name it same as your github username. That’s it, there you have your own portfolio repo. Now add as many content you want in readme.md file and it will be shown to home page of your github profile.
What kind of things you can include?
- You can include your own words which describe yourself.
- Social media profiles.
- Blogposts.
- Youtube videos.
- Projects you have worked.
- Contributions you are doing on various platform.
- Applications/websites that you have built.
If you are confused on what to include in readme file, here are some repos which has list of various readme files.
Interesting?
But now problem arise, imagine you are writing blogs on daily or weekly basis, or posting videos on youtube or other platform. Will you be able to update readme file on regular basis? Will you be able to keep it updated whenever you post blog/article/video?
Well, you can, but it’s bit difficult and you have to keep this in your bucket list whenever you post something.
Here comes the actual thing that I want to share.
What if we will make our github profile dynamic?
What if it will just read your blog feed or video feed and update readme file on regular basis by its own? Yes, we can do it by writing few piece of code.
Before going through further I will assume you are having basic knowledge of Github Action and Node.js, because this is what we are going to use further.
Let’s do some code now
First step will be to create .yml file in your repository inside .github/workflow
For this tutorial, I will name it as instagram-post-workflow.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
name: Instagram post on: workflow_dispatch: schedule: - cron: '0 13 * * *' jobs: build: runs-on: macOS-latest strategy: matrix: node-version: [12.x] steps: - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - name: Install dependencies run: | npm ci - name: Update README run: |- npm start cat README.md - name: Commit and push if changed run: |- git add . git diff git config --global user.email "example@gmail.com" git config --global user.name "Ravi Rupareliya" git commit -m "Instagram feed updated." -a || echo "No changes to commit" git push |
This is basically a cron job, which will run at 1PM UTC time. You can look into document for more detail about how you can set different schedule time
Once you are done with .yml file creation, you need to create package.json file and index.js on root of your repository.
package.json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
{ "name": "ravirupareliya", "version": "0.0.1", "description": "Instagram feed generate", "main": "feed.js", "scripts": { "start": "node index.js" }, "repository": { "type": "git", "url": "git+https://github.com/ravirupareliya/ravirupareliya.git" }, "author": "Ravi Rupareliya", "license": "GPL-3.0", "bugs": { "url": "https://github.com/ravirupareliya/ravirupareliya/issues" }, "homepage": "https://github.com/ravirupareliya/ravirupareliya#readme", "dependencies": { "@actions/core": "^1.2.4", "process": "latest", "axios": "^0.19.2" }, "devDependencies": { "jshint": "^2.11.1", "mocha": "^8.1.1", "parcel-bundler": "^1.12.4" } } |
index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
const process = require('process'); const axios = require('axios'); const core = require('@actions/core'); const fs = require('fs'); const exec = require('./exec'); const buildReadme = (previousContent, newContent) => { const tagToLookFor = `<!-- insta-feed:`; const closingTag = '-->'; const startOfOpeningTagIndex = previousContent.indexOf( `${tagToLookFor}START`, ); const endOfOpeningTagIndex = previousContent.indexOf( closingTag, startOfOpeningTagIndex, ); const startOfClosingTagIndex = previousContent.indexOf( `${tagToLookFor}END`, endOfOpeningTagIndex, ); if ( startOfOpeningTagIndex === -1 || endOfOpeningTagIndex === -1 || startOfClosingTagIndex === -1 ) { // Exit with error if comment is not found on the readme core.error( `Cannot find the comment tag on the readme:\n<!-- ${tagToLookFor}:START -->\n<!-- ${tagToLookFor}:END -->` ); process.exit(1); } return [ previousContent.slice(0, endOfOpeningTagIndex + closingTag.length), '\n', newContent, '\n', previousContent.slice(startOfClosingTagIndex), ].join(''); }; /** * Code to do git commit * @return {Promise<void>} */ const commitReadme = async () => { // Getting config const committerUsername = 'Ravi Rupareliya'; const committerEmail = 'example@gmail.com'; const commitMessage = 'Instagram feed updated.'; // Doing commit and push await exec('git', [ 'config', '--global', 'user.email', committerEmail, ]); await exec('git', ['config', '--global', 'user.name', committerUsername]); await exec('git', ['add', README_FILE_PATH]); await exec('git', ['commit', '-m', commitMessage]); await exec('git', ['push']); core.info("Readme updated successfully."); // Making job fail if one of the source fails process.exit(jobFailFlag ? 1 : 0); }; // Total no of posts to display on readme, all sources combined, default: 5 const TOTAL_POST_COUNT = 12; // Readme path, default: ./README.md const README_FILE_PATH = './README.md'; let postsArray = []; // Array to store posts let jobFailFlag = false; // Job status flag let siteUrl = 'https://www.instagram.com/ravi.rupareliya/?__a=1'; axios.get(siteUrl) .then(response => { const responsePosts = response.data.graphql.user.edge_owner_to_timeline_media.edges; postsArray = responsePosts .map((item) => { return { url: item.node.thumbnail_resources[0].src }; }); postsArray = postsArray.slice(0, TOTAL_POST_COUNT); if (postsArray.length > 0) { try { const readmeData = fs.readFileSync(README_FILE_PATH, "utf8"); const postListMarkdown = postsArray.reduce((acc, cur, index) => { // Default template: - [$title]($url) let startTag = '', endTag = '' if (index === 0 || index === 3 || index === 6 || index === 9) { startTag = '<p align=\"center\">\n' } if (index === 2 || index === 5 || index === 8 || index === 11) { endTag = '</p>\n' } return acc + startTag + `<img align="center" src=${cur.url} />\n` + endTag; }, ''); const newReadme = buildReadme(readmeData, postListMarkdown); // if there's change in readme file update it if (newReadme !== readmeData) { core.info('Writing to ' + README_FILE_PATH); fs.writeFileSync(README_FILE_PATH, newReadme); if (!process.env.TEST_MODE) { // noinspection JSIgnoredPromiseFromCall commitReadme(); } } else { core.info('No change detected, skipping'); process.exit(0); } } catch (e) { core.error(e); process.exit(1); } } else { core.info("0 blog posts fetched"); process.exit(jobFailFlag ? 1 : 0); } }) .catch(error => { core.error(' runner failed, please verify the configuration. Error:'); core.error(error); }); |
You will also need exec.js file,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
const {spawn} = require('child_process'); const exec = (cmd, args = [], options = {}) => new Promise((resolve, reject) => { console.log(`Started: ${cmd} ${args.join(' ')}`); const optionsToCLI = { ...options }; if (!optionsToCLI.stdio) { Object.assign(optionsToCLI, {stdio: ['inherit', 'inherit', 'inherit']}); } const app = spawn(cmd, args, optionsToCLI); app.on('close', (code) => { if (code !== 0) { const err = new Error(`Invalid status code: ${code}`); err.code = code; return reject(err); } return resolve(code); }); app.on('error', reject); }); module.exports = exec; |
This is not must need file, you can have this logic inside index.js also. This is for safe side we are creating separate file and validating commands.
Once you have above code, you are almost done with the things. Only part that needs to be done now is, adding tag in readme.md file. If you have observed index.js file, you might have seen we are looking for some tags in readme file to add content inside it.
Just add below tag in readme.md file
1 2 3 |
<!-- insta-feed:START--> <!-- insta-feed:END--> |
and you are done, all dynamic instagram feed will be added within this tag.

For detail code, you can look into my github repo Ravi Rupareliya
Reference links:
Do share your readme file in comment section, let’s see how creative you are and what have you done with readme file to portrait your profile.
Ravi Rupareliya
Latest posts by Ravi Rupareliya (see all)
- Make Your Github Profile Dynamic - August 25, 2020
- Dialogflow Entities With Actions on Google - May 7, 2020
- Actions on Google Using Cloud Functions - February 3, 2020