├── devops ├── appdev │ └── nodejs │ │ ├── public │ │ ├── hula_octocat.gif │ │ └── index.html │ │ ├── Dockerfile │ │ ├── function.js │ │ ├── server.js │ │ ├── package.json │ │ ├── __tests__ │ │ └── filterByTerm.spec.js │ │ └── .gitignore └── infra │ ├── aws │ └── main.tf │ └── azure │ └── main.tf ├── .github ├── CODEOWNERS ├── workflows │ ├── hello-github-actions.yml │ ├── action-send-slack-msg.yml │ ├── action-send-team-msg.yml │ ├── action-terraform-aws.yml │ ├── action-terraform-azure.yml │ ├── action-discover-projectboards.yml │ ├── action-update-issue.yml │ ├── action-infra-create-azure-webapp.yml │ ├── action-issueops-demo.yml │ ├── action-nodejs-cicd.yml │ └── action-clone-and-create-new-project.yml └── ISSUE_TEMPLATE │ └── demo-issueops-template.yml └── README.md /devops/appdev/nodejs/public/hula_octocat.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githubuniverseworkshops/GitHub-Actions-Planet-2021-Korea/HEAD/devops/appdev/nodejs/public/hula_octocat.gif -------------------------------------------------------------------------------- /devops/appdev/nodejs/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ghcr.io/githubuniverseworkshops/node:latest 2 | 3 | WORKDIR /app 4 | 5 | COPY ["package.json","package-lock.json*","./"] 6 | 7 | RUN npm install 8 | 9 | COPY . . 10 | 11 | CMD ["npm","start"] 12 | -------------------------------------------------------------------------------- /devops/appdev/nodejs/function.js: -------------------------------------------------------------------------------- 1 | function filterByTerm(inputArr, searchTerm) { 2 | return inputArr.filter(function(arrayElement){ 3 | return arrayElement.url.match(searchTerm); 4 | }); 5 | } 6 | 7 | module.exports = filterByTerm; 8 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # This is a comment. 2 | # Each line is a file pattern followed by one or more owners. 3 | 4 | # These owners will be the default owners for everything in 5 | # the repo. Unless a later match takes precedence, 6 | # @global-owner1 and @global-owner2 will be requested for 7 | # review when someone opens a pull request. 8 | * @bryantson 9 | -------------------------------------------------------------------------------- /devops/appdev/nodejs/server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | 4 | app.use(express.static("public")); 5 | 6 | app.get("/", (req, res) => { 7 | res.send("Hello, World"); 8 | }); 9 | 10 | const server = require("http").createServer({},app); 11 | 12 | server.listen(80, function() { 13 | console.log("HTTPS listening on port 80"); 14 | }); 15 | -------------------------------------------------------------------------------- /devops/appdev/nodejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodejs-app", 3 | "scripts": { 4 | "test": "jest --coverage --json --outputFile=./output.txt", 5 | "start": "node server.js" 6 | }, 7 | "dependencies": { 8 | "jest": "~27.0.5", 9 | "express": "~4.17.1" 10 | }, 11 | "jest": { 12 | "collectCoverage": true, 13 | "coverageReporters": ["html"] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.github/workflows/hello-github-actions.yml: -------------------------------------------------------------------------------- 1 | name: 안녕, 깃허브 액션 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | myName: 7 | description: "이름을 입력해 주세요" 8 | required: true 9 | default: '' 10 | 11 | jobs: 12 | Hello-GitHub-Actions: 13 | runs-on: ubuntu-latest 14 | name: 그냥 "안녕" 인사하고 싶었어요 ^^ 15 | 16 | steps: 17 | - name: 안녕하세요, 깃허브 액션 18 | run: | 19 | echo "따뜻한 환영 고마워요! 저의 이름은 ${{ github.event.inputs.myName }} 입니다" 20 | echo "신비로운 깃허브 액션에서 즐거운 시간을 보낼께요!" 21 | -------------------------------------------------------------------------------- /.github/workflows/action-send-slack-msg.yml: -------------------------------------------------------------------------------- 1 | name: 슬랙 메시지 보내기 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | greeting: 7 | description: "깃허브 액션에서 전송할 인사말을 적어 주세요!" 8 | required: true 9 | default: '' 10 | 11 | jobs: 12 | Hello-GitHub-Actions: 13 | runs-on: ubuntu-latest 14 | name: 슬랙 채널에 전송 15 | 16 | steps: 17 | - name: 깃허브 액션 - 안녕 인사 18 | id: slack 19 | uses: slackapi/slack-github-action@v1.14.0 20 | with: 21 | channel-id: 'github-universe' 22 | slack-message: '깃허브 액션 행성에서 인사합니다! 반가워요. ${{ github.event.inputs.greeting }}' 23 | env: 24 | SLACK_BOT_TOKEN: ${{ secrets.SLACK_TOKEN }} 25 | -------------------------------------------------------------------------------- /devops/appdev/nodejs/__tests__/filterByTerm.spec.js: -------------------------------------------------------------------------------- 1 | const filterByTerm = require("../function.js"); 2 | 3 | describe("Filter function", () => { 4 | test("Test 1", () => { 5 | const input = [ 6 | {id: "1", url: "http://www.google.com" }, 7 | {id: "2", url: "http://www.msn.com" }, 8 | {id: "3", url: "http://www.test.com"} 9 | ]; 10 | 11 | const output = [ { id: "3", url: "http://www.test.com" }]; 12 | 13 | expect(filterByTerm(input, "test")).toEqual(output); 14 | }); 15 | 16 | test("Test 2", () => { 17 | const input = [ 18 | {id: "1", url: "http://www.google.com" }, 19 | {id: "2", url: "http://www.msn.com" }, 20 | {id: "3", url: "http://www.test.com"} 21 | ]; 22 | 23 | const output = [ { id: "2", url: "http://www.msn.com" }]; 24 | 25 | expect(filterByTerm(input, "msn")).toEqual(output); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /.github/workflows/action-send-team-msg.yml: -------------------------------------------------------------------------------- 1 | name: 마소 팀즈 메시지 보내기 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | messageTeam: 7 | description: "깃허브 액션 행성에서 전송할 메시지를 적어주세요!" 8 | required: true 9 | default: '' 10 | jobs: 11 | 12 | Send-Message: 13 | runs-on: ubuntu-latest 14 | name: 팀스 메시지 전공 15 | 16 | steps: 17 | - name: 마이크로소프트 팀스로 메시지 보내기 18 | uses: aliencube/microsoft-teams-actions@v0.8.0 19 | with: 20 | webhook_uri: ${{ secrets.MICROSOFT_TEAM_URL }} 21 | title: Hello, GitHub Actions planet 22 | summary: 팀스 메시지 전송 데모 입니다 23 | text: 깃허브 액션 행성으로 오신걸 환영합니다. 불가능이란 없고 다양한 배움과 네트워크를 통해 성장 할수 있는 곳. 그리고 졸귀 문어 고양이도 찾을 수 있습니다. 24 | theme_color: red 25 | sections: '[{ "activityTitle": "${{ github.event.inputs.messageTeam }}" }]' 26 | actions: '[{ "@type": "OpenUri", "name": "깃허브 유니버스", "targets": [{ "os": "default", "uri": "https://githubuniverse.com" }] }]' 27 | -------------------------------------------------------------------------------- /.github/workflows/action-terraform-aws.yml: -------------------------------------------------------------------------------- 1 | name: AWS 테라폼으로 배포 2 | 3 | 4 | on: 5 | workflow_dispatch: 6 | inputs: 7 | provision_aws: 8 | description: "AWS 인프라 리소스를 만드시겠습니까? (네/아니요)" 9 | required: true 10 | default: '네' 11 | 12 | defaults: 13 | run: 14 | working-directory: devops/infra/aws 15 | 16 | env: 17 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 18 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 19 | 20 | jobs: 21 | Terraform-Script: 22 | runs-on: ubuntu-latest 23 | name: 테라폼 실행 24 | if: ${{ github.event.inputs.provision_aws == '네' }} 25 | 26 | steps: 27 | - name: 파일 체크아웃 28 | uses: actions/checkout@v2 29 | 30 | - name: 테라폼 셋업 31 | uses: hashicorp/setup-terraform@v1 32 | 33 | - name: 테라폼 설정 34 | id: init 35 | run: terraform init 36 | 37 | - name: 테라폼 확인 38 | id: validate 39 | run: terraform validate -no-color 40 | 41 | - name: 테라폼 계획 42 | id: plan 43 | run: terraform plan -no-color 44 | continue-on-error: false 45 | 46 | - name: 테라폼 적용 47 | id: apply 48 | run: terraform apply -auto-approve 49 | -------------------------------------------------------------------------------- /.github/workflows/action-terraform-azure.yml: -------------------------------------------------------------------------------- 1 | name: Azure 테라폼으로 배포 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | provision_azure: 7 | description: "Azure 인프라 리소스를 만드시겠습니까? (네/아니요)" 8 | required: true 9 | default: '네' 10 | 11 | defaults: 12 | run: 13 | working-directory: devops/infra/azure 14 | 15 | env: 16 | ARM_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID }} 17 | ARM_CLIENT_SECRET: ${{ secrets.ARM_CLIENT_SECRET }} 18 | ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID }} 19 | ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }} 20 | 21 | jobs: 22 | Terraform-Script: 23 | runs-on: ubuntu-latest 24 | name: 테라폼 실행 25 | if: ${{ github.event.inputs.provision_azure == '네' }} 26 | 27 | steps: 28 | - name: 파일 체크아웃 29 | uses: actions/checkout@v2 30 | 31 | - name: 테라폼 셋업 32 | uses: hashicorp/setup-terraform@v1 33 | 34 | - name: 테라폼 설정 35 | id: init 36 | run: terraform init 37 | 38 | - name: 테라폼 확인 39 | id: validate 40 | run: terraform validate -no-color 41 | 42 | - name: 테라폼 계획 43 | id: plan 44 | run: terraform plan -no-color 45 | continue-on-error: false 46 | 47 | - name: 테라폼 적용 48 | id: apply 49 | run: terraform apply -auto-approve 50 | -------------------------------------------------------------------------------- /.github/workflows/action-discover-projectboards.yml: -------------------------------------------------------------------------------- 1 | name: 만들어진 프로젝트 보드 발견 2 | on: 3 | workflow_dispatch: 4 | inputs: 5 | ownerProject: 6 | description: "현재 만들어진 프로젝트들을 찾아볼까요?" 7 | default: "네" 8 | required: true 9 | 10 | 11 | permissions: 12 | actions: none 13 | checks: none 14 | contents: none 15 | deployments: none 16 | issues: none 17 | packages: none 18 | pull-requests: none 19 | repository-projects: read 20 | security-events: none 21 | statuses: none 22 | 23 | jobs: 24 | discover-different-projects: 25 | name: 프로젝트 보드 찾기 26 | runs-on: ubuntu-latest 27 | steps: 28 | - name: 프로젝트 보드 찾기 29 | uses: actions/github-script@v4.1 30 | with: 31 | github-token: ${{ secrets.GITHUB_TOKEN }} 32 | previews: 'inertia' # 깃허브 프로젝트 API 를 위해선 필수 33 | script: | 34 | try { 35 | listProjects = await github.projects.listForRepo({ 36 | owner: context.payload.organization.login , 37 | repo: context.payload.repository.name , 38 | per_page: 100 39 | }) 40 | 41 | } catch(err) { 42 | throw err 43 | } 44 | 45 | var count = 0 46 | console.log("| 프로젝트 개수 | 프로젝트 이름 | 고유 프로젝트 아이디 | 만든이 |") 47 | listProjects.data.forEach(function(project) { 48 | console.log("| 프로젝트 " + count + " | " + project.name + " | " + project.id + " | " + project.creator.login + " |") 49 | ++ count 50 | }) 51 | 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 안녕하세요 여러분, 전 문어 고양이 "모나" 에요! :octocat: 🐙 🐱 2 | ## "깃허브 액션 행성에 오신걸 환영 합니다" 👋 3 | 4 | ![mona-ocean](https://user-images.githubusercontent.com/5396174/137653022-15381990-c9eb-45e7-af82-4881fc3f0d19.gif) 5 | 6 | 저흰 **깃허브 액션 행성** 을 항해하며 총 7 개의 대륙에서 결코 쉽진 않지만 흥미로운 깃허브 액션 미션들을 하나 하나 발견하게 될것입니다. 우리가 같이 풀어 나가야 할 깃허브 액션 미션들은 깃허브 이슈에 설명 되어 있습니다. 7 | 8 | ## 난이도 있는 미션들을 달성하며 🏃 문제들을 풀어 나가겠지만 ⛰️ 🔥 재미 있을거에요! 🎮 9 | 10 | ![mona](https://user-images.githubusercontent.com/5396174/138539357-bb27a65d-1fe8-4dbb-8502-9e73f1f2bbd0.gif) 11 | 12 | ## 🍿 저희의 운항 노선 입니다 13 | 1. [**깃허브 액션 대륙** - 안녕, 깃허브 액션 행성](https://github.com/githubuniverseworkshops/GitHub-Actions-Planet-2021-Korea/issues/1) 14 | 2. [**프로젝트 메니지먼트 대륙** - 제일 중요한건 계획이죠! 깃허브 프로젝트 보드를 통해 이 행성에서 살아남기 위한 계획을 같이 세워요](https://github.com/githubuniverseworkshops/GitHub-Actions-Planet-2021-Korea/issues/2) 15 | 3. [**그룹 액션 대륙** - 각자 필요한 프로젝트 보드를 만들어 보자구!](https://github.com/githubuniverseworkshops/GitHub-Actions-Planet-2021-Korea/issues/3) 16 | 4. [**이슈옵스 대륙** - 이슈옵스를 통해서 모범 기준을 준수하는 레포를 만들어 봐요](https://github.com/githubuniverseworkshops/GitHub-Actions-Planet-2021-Korea/issues/4) 17 | 5. [**메시지 전송 대륙** - 대륙횡단 하기 전에 우리 행성 지구에게 메시지를 전송해요](https://github.com/githubuniverseworkshops/GitHub-Actions-Planet-2021-Korea/issues/5) 18 | 6. [**인프라 데브옵스 대륙** - 테라폼을 사용해서 Azure 클라우드에 인프라 리소스를 생산 해봐요](https://github.com/githubuniverseworkshops/GitHub-Actions-Planet-2021-Korea/issues/6) 19 | 7. [**앱데브 데브옵스 대륙** - CI/CD 프로세스를 통해서 NodeJs 앱을 Azure 클라우드에 배포 해봐요](https://github.com/githubuniverseworkshops/GitHub-Actions-Planet-2021-Korea/issues/7) 20 | 8. [**마무리 데륙** - 여기서 굿바이지만 끝은 아니죠...](https://github.com/githubuniverseworkshops/GitHub-Actions-Planet-2021-Korea/issues/8) 21 | -------------------------------------------------------------------------------- /.github/workflows/action-update-issue.yml: -------------------------------------------------------------------------------- 1 | name: 이슈 업데이트 2 | 3 | on: 4 | issues: 5 | types: closed 6 | 7 | jobs: 8 | Manage-Issues: 9 | runs-on: ubuntu-latest 10 | name: 이슈 메니지먼트 11 | 12 | steps: 13 | - name: 클로즈 이슈 14 | uses: actions/github-script@v5 15 | with: 16 | script: | 17 | // 이슈 닫기 18 | github.rest.issues.update({ 19 | owner: context.payload.organization.login, 20 | repo: context.payload.repository.name, 21 | issue_number: context.issue.number, 22 | state: "closed" 23 | }) 24 | 25 | 26 | - name: 오픈 이슈 27 | uses: actions/github-script@v5 28 | with: 29 | script: | 30 | // 다음 이슈 오픈 31 | github.rest.issues.update({ 32 | owner: context.payload.organization.login, 33 | repo: context.payload.repository.name, 34 | issue_number: context.issue.number + 1, 35 | state: "open" 36 | }) 37 | 38 | - name: 이슈 커멘트 39 | uses: actions/github-script@v5 40 | with: 41 | script: | 42 | // 이슈 커멘트 43 | github.rest.issues.createComment({ 44 | owner: context.payload.organization.login, 45 | repo: context.payload.repository.name, 46 | issue_number: context.issue.number, 47 | body: "# 축하합니다. " + context.payload.issue.title + " 에서의 미션을 완성하셨습니다\n ![image](https://user-images.githubusercontent.com/5396174/137656679-43d43513-beef-4e4a-bcd9-fec7941d07a5.png)" + "\n ## [다음 대륙으로 이동할까요?](https://github.com/" + context.payload.organization.login + "/" + context.payload.repository.name + "/issues/" + (context.payload.issue.number + 1) + ")" 48 | }) 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/demo-issueops-template.yml: -------------------------------------------------------------------------------- 1 | name: 이슈옵스를 통한 레포 만들기 2 | description: 이슈옵스는 사용자로 부터 입력 값을 받은 후 레포를 샘플 템플릿 레포를 통해 만들고 다양한 세팅을 설정 합니다 3 | title: "[이슈옵스]: " 4 | labels: ["issueops", "triage"] 5 | assignees: 6 | - bryantson 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: | 11 | 이 레포트를 완성해주시면 감사하겠습니다 12 | - type: input 13 | id: new-repository 14 | attributes: 15 | label: 새로운 레포 이름 16 | description: 새로운 레포의 영문 이름으로 적어 주세요 17 | placeholder: 예. new-repository 18 | validations: 19 | required: true 20 | - type: dropdown 21 | id: template-repository 22 | attributes: 23 | label: 템플릿 레포 24 | description: 사용하고 싶은 템플릿 레포를 골라주세요 25 | options: 26 | - sample-template-repository-universe 27 | validations: 28 | required: true 29 | - type: input 30 | id: team 31 | attributes: 32 | label: 팀 권한 33 | description: 관리자 권한을 적용한 팀 이름을 적어주세요 34 | placeholder: 예. Avengers 35 | validations: 36 | required: false 37 | 38 | - type: checkboxes 39 | id: enable-issues 40 | attributes: 41 | label: Issue 사용하기 42 | description: Issue 를 사용 가능하게 만들까요? 43 | options: 44 | - label: 네, issue 를 사용 가능하길 원합니다 45 | required: false 46 | 47 | - type: checkboxes 48 | id: enable-project 49 | attributes: 50 | label: Project 보드 사용하기 51 | description: Project 보드를 사용 가능하게 만들까요? 52 | options: 53 | - label: 네, project Board 를 사용 가능하길 원합니다 54 | required: false 55 | 56 | - type: checkboxes 57 | id: enable-squash-merge 58 | attributes: 59 | label: Squash Merge 를 사용하기 60 | description: squash merge 를 사용 가능하게 만들까요? 61 | options: 62 | - label: 네, squash merge 를 사용 가능하길 원합니다 63 | required: false 64 | 65 | - type: checkboxes 66 | id: enable-forking 67 | attributes: 68 | label: Fork 를 사용하기 69 | description: Fork 를 사용 가능하게 만들까요? 70 | options: 71 | - label: 네, Fork 를 사용 가능하길 원합니다 72 | required: false 73 | 74 | -------------------------------------------------------------------------------- /devops/appdev/nodejs/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 안녕, 깃허브 액션 4 | 5 | 6 | 7 | 8 |
9 | 18 | 19 |

깃허브 액션 행성에 오신걸 환영합니다! - 문어 고양이 "모나"

20 | 21 |

깃허브 액션을 통해서 NodeJS 앱을 배포 하는 일에 성공하였습니다!

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 |
#페키지 종류웹 주소문서
1컨테이너 리지스트리ghrc.io컨테이너 리지스트리 문서
2NPM 리지스트리https://npm.pkg.github.comNPM 리지스트리 문서
3루비 Gems 리지스트리https://rubygems.pkg.github.com루비 Gems 문서
58 | 59 |
60 | 61 | 62 | -------------------------------------------------------------------------------- /.github/workflows/action-infra-create-azure-webapp.yml: -------------------------------------------------------------------------------- 1 | name: Azure 웹앱 배포 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | provision_azure: 7 | description: "Azure 웹앱을 배포 할까요? (네/아니요)" 8 | required: true 9 | default: '네' 10 | 11 | env: 12 | AZURE_RESOURCE_GROUP: GitHubUniverse2021-AppDevDemo-group 13 | AZURE_APP_PLAN: actions-bryant-deployment 14 | AZURE_LOCATION: '"Central US"' 15 | ################################################# 16 | ### 사용자 설정 값들은 아래에서 찾을수 있습니다 ### 17 | ################################################# 18 | ################################################# 19 | AZURE_WEBAPP_NAME: sample-nodejs-app-bryant 20 | ################################################# 21 | 22 | jobs: 23 | setup-up-azure-resources: 24 | name: Azure 웹앱 배포하기 25 | runs-on: ubuntu-latest 26 | 27 | if: ${{ github.event.inputs.provision_azure == '네' }} 28 | 29 | steps: 30 | - name: 레포 체크아웃 31 | uses: actions/checkout@v2 32 | 33 | - name: Azure 로그인 34 | uses: azure/login@v1 35 | with: 36 | creds: ${{ secrets.AZURE_CREDENTIALS }} 37 | 38 | - name: Azure 리소스 그룹 만들기 39 | if: success() 40 | run: | 41 | az group create --location ${{env.AZURE_LOCATION}} --name ${{env.AZURE_RESOURCE_GROUP}} --subscription ${{secrets.ARM_SUBSCRIPTION_ID}} 42 | 43 | - name: Azure 앱 서비스 플랜 만들기 44 | if: success() 45 | run: | 46 | az appservice plan create --resource-group ${{env.AZURE_RESOURCE_GROUP}} --name ${{env.AZURE_APP_PLAN}} --is-linux --sku F1 --subscription ${{secrets.ARM_SUBSCRIPTION_ID }} 47 | 48 | - name: Azure 웹앱 만들기 49 | if: success() 50 | run: | 51 | az webapp create --resource-group ${{ env.AZURE_RESOURCE_GROUP }} --plan ${{ env.AZURE_APP_PLAN }} --name ${{ env.AZURE_WEBAPP_NAME }} --deployment-container-image-name nginx --subscription ${{secrets.ARM_SUBSCRIPTION_ID}} 52 | 53 | - name: 깃허브 페키지를 위한 웹앱 설정 54 | if: success() 55 | run: | 56 | az webapp config container set --docker-custom-image-name nginx --docker-registry-server-password ${{secrets.GITHUB_TOKEN}} --docker-registry-server-url https://ghcr.io --docker-registry-server-user ${{github.actor}} --name ${{ env.AZURE_WEBAPP_NAME }} --resource-group ${{ env.AZURE_RESOURCE_GROUP }} --subscription ${{secrets.ARM_SUBSCRIPTION_ID}} 57 | -------------------------------------------------------------------------------- /devops/appdev/nodejs/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # Snowpack dependency directory (https://snowpack.dev/) 45 | web_modules/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | .parcel-cache 78 | 79 | # Next.js build output 80 | .next 81 | out 82 | 83 | # Nuxt.js build / generate output 84 | .nuxt 85 | dist 86 | 87 | # Gatsby files 88 | .cache/ 89 | # Comment in the public line in if your project uses Gatsby and not Next.js 90 | # https://nextjs.org/blog/next-9-1#public-directory-support 91 | # public 92 | 93 | # vuepress build output 94 | .vuepress/dist 95 | 96 | # Serverless directories 97 | .serverless/ 98 | 99 | # FuseBox cache 100 | .fusebox/ 101 | 102 | # DynamoDB Local files 103 | .dynamodb/ 104 | 105 | # TernJS port file 106 | .tern-port 107 | 108 | # Stores VSCode versions used for testing VSCode extensions 109 | .vscode-test 110 | 111 | # yarn v2 112 | .yarn/cache 113 | .yarn/unplugged 114 | .yarn/build-state.yml 115 | .yarn/install-state.gz 116 | .pnp.* 117 | -------------------------------------------------------------------------------- /devops/infra/aws/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_launch_configuration" "example" { 2 | image_id = "ami-0c55b159cbfafe1f0" 3 | instance_type = "t2.micro" 4 | security_groups = [aws_security_group.instance.id] 5 | 6 | user_data = <<-EOF 7 | #!/bin/bash 8 | echo "Hello, World" > index.html 9 | nohup busybox httpd -f -p ${var.server_port} & 10 | EOF 11 | 12 | lifecycle { 13 | create_before_destroy = true 14 | } 15 | } 16 | 17 | resource "aws_autoscaling_group" "example" { 18 | launch_configuration = aws_launch_configuration.example.name 19 | vpc_zone_identifier = data.aws_subnet_ids.default.ids 20 | 21 | target_group_arns = [aws_lb_target_group.asg.arn] 22 | health_check_type = "ELB" 23 | 24 | min_size = 2 25 | max_size = 10 26 | 27 | tag { 28 | key = "Name" 29 | value = "terraform-cicd-demo" 30 | propagate_at_launch = true 31 | } 32 | } 33 | 34 | data "aws_vpc" "default" { 35 | default = true 36 | } 37 | 38 | data "aws_subnet_ids" "default" { 39 | vpc_id = data.aws_vpc.default.id 40 | } 41 | 42 | provider "aws" { 43 | region= "us-east-2" 44 | } 45 | 46 | variable "server_port" { 47 | description = "The port the server will be used for HTTP requests" 48 | type = number 49 | default = 8080 50 | } 51 | 52 | output "alb_dns_name" { 53 | value = aws_lb.example.dns_name 54 | description = "The domain name of the laod balancer" 55 | } 56 | 57 | resource "aws_security_group" "instance" { 58 | name = "terraform-cicd-demo-instance" 59 | 60 | ingress { 61 | from_port = var.server_port 62 | to_port = var.server_port 63 | protocol = "tcp" 64 | cidr_blocks = ["0.0.0.0/0"] 65 | } 66 | } 67 | 68 | resource "aws_lb" "example" { 69 | name = "terraform-cicd-demo-lb" 70 | load_balancer_type = "application" 71 | subnets = data.aws_subnet_ids.default.ids 72 | security_groups = [aws_security_group.alb.id] 73 | } 74 | 75 | resource "aws_lb_listener" "http" { 76 | load_balancer_arn = aws_lb.example.arn 77 | port = 80 78 | protocol = "HTTP" 79 | 80 | default_action { 81 | type = "fixed-response" 82 | 83 | fixed_response { 84 | content_type = "text/plain" 85 | message_body = "404: page not found" 86 | status_code = 404 87 | } 88 | } 89 | } 90 | 91 | resource "aws_security_group" "alb" { 92 | name = "terraform-cicd-demo-alb" 93 | 94 | # Allow inbound HTTP requests 95 | ingress { 96 | from_port = 80 97 | to_port = 80 98 | protocol = "tcp" 99 | cidr_blocks = ["0.0.0.0/0"] 100 | } 101 | 102 | egress { 103 | from_port = 0 104 | to_port = 0 105 | protocol = "-1" 106 | cidr_blocks = ["0.0.0.0/0"] 107 | } 108 | } 109 | 110 | resource "aws_lb_target_group" "asg" { 111 | name = "terraform-cicd-demo-tg" 112 | port = var.server_port 113 | protocol = "HTTP" 114 | vpc_id = data.aws_vpc.default.id 115 | 116 | health_check { 117 | path = "/" 118 | protocol = "HTTP" 119 | matcher = "200" 120 | interval = 15 121 | timeout = 3 122 | healthy_threshold = 2 123 | unhealthy_threshold = 2 124 | } 125 | } 126 | 127 | resource "aws_lb_listener_rule" "asg" { 128 | listener_arn = aws_lb_listener.http.arn 129 | priority = 100 130 | 131 | condition { 132 | path_pattern { 133 | values = ["*"] 134 | } 135 | } 136 | 137 | action { 138 | type = "forward" 139 | target_group_arn = aws_lb_target_group.asg.arn 140 | } 141 | } 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /.github/workflows/action-issueops-demo.yml: -------------------------------------------------------------------------------- 1 | name: 이슈옵스를 통해서 레포 만들기 2 | on: 3 | issues: 4 | types: [opened] 5 | 6 | jobs: 7 | Create-Repo: 8 | name: 레포 만들기 9 | runs-on: ubuntu-latest 10 | if: contains(github.event.issue.labels.*.name, 'issueops') 11 | steps: 12 | 13 | - name: 깃허브 레포를 만듭니다 14 | uses: actions/github-script@v5 15 | with: 16 | github-token: ${{ secrets.CUSTOM_TOKEN }} 17 | script: | 18 | 19 | bodySplit = context.payload.issue.body.split("\n") 20 | 21 | let newRepo = bodySplit[2] 22 | let templateRepo = bodySplit[6] 23 | let teams = bodySplit[10].split(",") 24 | let enableIssue = bodySplit[14].includes("X") 25 | let enableProject = bodySplit[18].includes("X") 26 | let enableSquashMerge = bodySplit[22].includes("X") 27 | let enableForking = bodySplit[26].includes("X") 28 | 29 | try { 30 | await github.rest.repos.createUsingTemplate({ 31 | template_owner: context.payload.organization.login, 32 | template_repo: templateRepo, 33 | name: newRepo, 34 | owner: context.payload.organization.login, 35 | description: "깃허브 액션을 통해 만든 새로운 레포 ", 36 | include_all_branches: true, 37 | private: false 38 | }) 39 | } catch(err) { 40 | throw err 41 | } 42 | 43 | try { 44 | await github.rest.repos.update({ 45 | owner: context.payload.organization.login, 46 | private: true, 47 | repo: newRepo, 48 | has_issues: enableIssue, 49 | has_projects: enableProject, 50 | allow_squash_merge: enableSquashMerge 51 | }) 52 | } catch(err) { 53 | throw err 54 | } 55 | 56 | try { 57 | await github.rest.repos.updateBranchProtection({ 58 | "owner": context.payload.organization.login, 59 | "repo": newRepo, 60 | "branch": 'main', 61 | "required_status_checks": { "strict": true, "contexts": [] }, 62 | "enforce_admins": true, 63 | "required_pull_request_reviews": {"dismissal_restrictions": {"users":[], "teams":[]}, "dismiss_stale_reviews": false, "require_code_owner_reviews": true, "required_approving_review_count": 1}, 64 | "restrictions" : { "users": [], "teams": [], "apps": [] } 65 | }) 66 | } catch(err) { 67 | throw err 68 | } 69 | 70 | try { 71 | await github.rest.teams.addOrUpdateRepoPermissionsInOrg({ 72 | org: context.payload.organization.login, 73 | owner: context.payload.organization.login, 74 | repo: newRepo, 75 | team_slug: teams, 76 | permission: 'admin' 77 | }) 78 | } catch(err) { 79 | throw err 80 | } 81 | 82 | try { 83 | await github.rest.issues.createComment({ 84 | issue_number: context.issue.number, 85 | owner: context.repo.owner, 86 | repo: context.repo.repo, 87 | body: '새로운 레포는 다음 주소에서 : https://github.com/' + context.payload.organization.login + "/" + newRepo 88 | }) 89 | } catch(err) { 90 | throw err 91 | } 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /.github/workflows/action-nodejs-cicd.yml: -------------------------------------------------------------------------------- 1 | name: NodeJS CI/CD 프로세스 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | test-nodejs: 7 | description: "NodeJS CI/CD 프로세스를 실행할까요? (네/아니요)" 8 | required: true 9 | default: '네' 10 | env: 11 | DOCKER_IMAGE_NAME: my-custom-nodeapp 12 | DOCKER_IMAGE_VERSION: ${{ github.sha }} 13 | AZURE_WEBAPP_NAME: sample-nodejs-app-bryant 14 | IMAGE_REGISTRY_URL: ghcr.io 15 | AZURE_RESOURCE_GROUP: GitHubUniverse2021-AppDevDemo-group 16 | 17 | defaults: 18 | run: 19 | working-directory: devops/appdev/nodejs 20 | 21 | jobs: 22 | NodeJS-Test: 23 | runs-on: ubuntu-latest 24 | name: NodeJS 빌드 25 | if: ${{ github.event.inputs.test-nodejs == '네' }} 26 | 27 | steps: 28 | - name: 파일 체크아웃 29 | uses: actions/checkout@v2 30 | 31 | - name: NodeJS 셋업 32 | uses: actions/setup-node@v2 33 | with: 34 | node-version: ${{ matrix.node-version }} 35 | 36 | - name: NPM 모듈 설치 37 | run: npm install 38 | 39 | - name: NPM 테스트 돌리기 40 | run: npm test 41 | 42 | - name: 테스트 결과를 깃허브 아티팩트로 올리기 43 | uses: actions/upload-artifact@main 44 | with: 45 | name: 테스트 결과 46 | path: ${{ github.workspace }}/devops/appdev/nodejs/output.txt 47 | 48 | - name: 깃허브 아티팩트에 테스트 카버리지 올리기 49 | uses: actions/upload-artifact@main 50 | with: 51 | name: 테스트 카버리지 결과 52 | path: ${{ github.workspace }}/devops/appdev/nodejs/coverage 53 | 54 | Build-Image: 55 | needs: NodeJS-Test 56 | runs-on: ubuntu-latest 57 | name: 컨테이너 이미지 빌드 58 | 59 | steps: 60 | - name: 파일 체크 아웃 61 | uses: actions/checkout@v2 62 | 63 | - name: 깃허브 컨테이너 리지스트리 로그인 64 | uses: docker/login-action@v1 65 | with: 66 | registry: ghcr.io 67 | username: ${{ github.repository_owner }} 68 | password: ${{ secrets.TOKEN_PACKAGE }} 69 | 70 | - name: 빌드 그리고 푸쉬 71 | id: docker_build 72 | uses: docker/build-push-action@v2 73 | with: 74 | context: devops/appdev/nodejs 75 | push: true 76 | tags: ghcr.io/githubuniverseworkshops/nodejs/${{ env.DOCKER_IMAGE_NAME }}:${{ env.DOCKER_IMAGE_VERSION }} 77 | 78 | Deploy-Azure: 79 | runs-on: ubuntu-latest 80 | needs: Build-Image 81 | name: 컨테이너를 Azure 웹앱에 배포 82 | steps: 83 | - name: Azure CLI 로 로그인 84 | uses: azure/login@v1 85 | with: 86 | creds: ${{ secrets.AZURE_CREDENTIALS }} 87 | 88 | - name: GHCR 리지스트리 로그인 89 | uses: azure/docker-login@v1 90 | with: 91 | login-server: ghcr.io 92 | username: ${{ github.actor }} 93 | password: ${{ secrets.TOKEN_PACKAGE }} 94 | 95 | - name: 웹앱 ACR 인증 96 | uses: Azure/appservice-settings@v1 97 | with: 98 | app-name: ${{ env.AZURE_WEBAPP_NAME }} 99 | app-settings-json: | 100 | [ 101 | { 102 | "name": "DOCKER_REGISTRY_SERVER_PASSWORD", 103 | "value": "${{ secrets.TOKEN_PACKAGE }}", 104 | "slotSetting": false 105 | }, 106 | { 107 | "name": "DOCKER_REGISTRY_SERVER_URL", 108 | "value": "https://ghcr.io", 109 | "slotSetting": false 110 | }, 111 | { 112 | "name": "DOCKER_REGISTRY_SERVER_USERNAME", 113 | "value": "${{ github.actor }}", 114 | "slotSetting": false 115 | } 116 | ] 117 | 118 | - name: Azure 웹앱 컨테이너로 배포 119 | uses: azure/webapps-deploy@v2 120 | with: 121 | app-name: ${{ env.AZURE_WEBAPP_NAME }} 122 | images: ${{ env.IMAGE_REGISTRY_URL }}/githubuniverseworkshops/nodejs/${{ env.DOCKER_IMAGE_NAME }}:${{ env.DOCKER_IMAGE_VERSION }} 123 | 124 | 125 | -------------------------------------------------------------------------------- /devops/infra/azure/main.tf: -------------------------------------------------------------------------------- 1 | # Configure the Microsoft Azure Provider 2 | terraform { 3 | required_providers { 4 | azurerm = { 5 | source = "hashicorp/azurerm" 6 | version = "~>2.0" 7 | } 8 | } 9 | } 10 | provider "azurerm" { 11 | features {} 12 | } 13 | 14 | # Create a resource group if it doesn't exist 15 | resource "azurerm_resource_group" "myterraformgroup" { 16 | name = "GitHubUniverseRG" 17 | location = "eastus" 18 | 19 | tags = { 20 | environment = "Terraform Demo" 21 | } 22 | } 23 | 24 | # Create virtual network 25 | resource "azurerm_virtual_network" "myterraformnetwork" { 26 | name = "myVnet" 27 | address_space = ["10.0.0.0/16"] 28 | location = "eastus" 29 | resource_group_name = azurerm_resource_group.myterraformgroup.name 30 | 31 | tags = { 32 | environment = "Terraform Demo" 33 | } 34 | } 35 | 36 | # Create subnet 37 | resource "azurerm_subnet" "myterraformsubnet" { 38 | name = "mySubnet" 39 | resource_group_name = azurerm_resource_group.myterraformgroup.name 40 | virtual_network_name = azurerm_virtual_network.myterraformnetwork.name 41 | address_prefixes = ["10.0.1.0/24"] 42 | } 43 | 44 | # Create public IPs 45 | resource "azurerm_public_ip" "myterraformpublicip" { 46 | name = "myPublicIP" 47 | location = "eastus" 48 | resource_group_name = azurerm_resource_group.myterraformgroup.name 49 | allocation_method = "Dynamic" 50 | 51 | tags = { 52 | environment = "Terraform Demo" 53 | } 54 | } 55 | 56 | # Create Network Security Group and rule 57 | resource "azurerm_network_security_group" "myterraformnsg" { 58 | name = "myNetworkSecurityGroup" 59 | location = "eastus" 60 | resource_group_name = azurerm_resource_group.myterraformgroup.name 61 | 62 | security_rule { 63 | name = "SSH" 64 | priority = 1001 65 | direction = "Inbound" 66 | access = "Allow" 67 | protocol = "Tcp" 68 | source_port_range = "*" 69 | destination_port_range = "22" 70 | source_address_prefix = "*" 71 | destination_address_prefix = "*" 72 | } 73 | 74 | tags = { 75 | environment = "Terraform Demo" 76 | } 77 | } 78 | 79 | # Create network interface 80 | resource "azurerm_network_interface" "myterraformnic" { 81 | name = "myNIC" 82 | location = "eastus" 83 | resource_group_name = azurerm_resource_group.myterraformgroup.name 84 | 85 | ip_configuration { 86 | name = "myNicConfiguration" 87 | subnet_id = azurerm_subnet.myterraformsubnet.id 88 | private_ip_address_allocation = "Dynamic" 89 | public_ip_address_id = azurerm_public_ip.myterraformpublicip.id 90 | } 91 | 92 | tags = { 93 | environment = "Terraform Demo" 94 | } 95 | } 96 | 97 | # Connect the security group to the network interface 98 | resource "azurerm_network_interface_security_group_association" "example" { 99 | network_interface_id = azurerm_network_interface.myterraformnic.id 100 | network_security_group_id = azurerm_network_security_group.myterraformnsg.id 101 | } 102 | 103 | # Generate random text for a unique storage account name 104 | resource "random_id" "randomId" { 105 | keepers = { 106 | # Generate a new ID only when a new resource group is defined 107 | resource_group = azurerm_resource_group.myterraformgroup.name 108 | } 109 | 110 | byte_length = 8 111 | } 112 | 113 | # Create storage account for boot diagnostics 114 | resource "azurerm_storage_account" "mystorageaccount" { 115 | name = "diag${random_id.randomId.hex}" 116 | resource_group_name = azurerm_resource_group.myterraformgroup.name 117 | location = "eastus" 118 | account_tier = "Standard" 119 | account_replication_type = "LRS" 120 | 121 | tags = { 122 | environment = "Terraform Demo" 123 | } 124 | } 125 | 126 | # Create (and display) an SSH key 127 | resource "tls_private_key" "example_ssh" { 128 | algorithm = "RSA" 129 | rsa_bits = 4096 130 | } 131 | output "tls_private_key" { 132 | value = tls_private_key.example_ssh.private_key_pem 133 | sensitive = true 134 | } 135 | 136 | # Create virtual machine 137 | resource "azurerm_linux_virtual_machine" "myterraformvm" { 138 | name = "myVM" 139 | location = "eastus" 140 | resource_group_name = azurerm_resource_group.myterraformgroup.name 141 | network_interface_ids = [azurerm_network_interface.myterraformnic.id] 142 | size = "Standard_DS1_v2" 143 | 144 | os_disk { 145 | name = "myOsDisk" 146 | caching = "ReadWrite" 147 | storage_account_type = "Premium_LRS" 148 | } 149 | 150 | source_image_reference { 151 | publisher = "Canonical" 152 | offer = "UbuntuServer" 153 | sku = "18.04-LTS" 154 | version = "latest" 155 | } 156 | 157 | computer_name = "myvm" 158 | admin_username = "azureuser" 159 | disable_password_authentication = true 160 | 161 | admin_ssh_key { 162 | username = "azureuser" 163 | public_key = tls_private_key.example_ssh.public_key_openssh 164 | } 165 | 166 | boot_diagnostics { 167 | storage_account_uri = azurerm_storage_account.mystorageaccount.primary_blob_endpoint 168 | } 169 | 170 | tags = { 171 | environment = "Terraform Demo" 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /.github/workflows/action-clone-and-create-new-project.yml: -------------------------------------------------------------------------------- 1 | name: 프로젝트 보드 카피 2 | on: 3 | workflow_dispatch: 4 | inputs: 5 | projectID: 6 | description: "카피하실 프로젝트 보드 아이디를 적어 주세요" 7 | default: "13877300" 8 | required: true 9 | descNewProject: 10 | description: "깃허브 아이드를 적어 주세요" 11 | default: "@깃허브아이디" 12 | required: true 13 | 14 | permissions: 15 | actions: none 16 | checks: none 17 | contents: none 18 | deployments: none 19 | issues: none 20 | packages: none 21 | pull-requests: none 22 | repository-projects: write 23 | security-events: none 24 | statuses: none 25 | 26 | env: 27 | PROJECT_ID: ${{ github.event.inputs.projectID }} 28 | 29 | jobs: 30 | newProject: 31 | name: 깃허브 프로젝트 카피 32 | runs-on: ubuntu-latest 33 | steps: 34 | - name: 깃허브 프로젝트 카피 35 | uses: actions/github-script@v4.1 36 | with: 37 | github-token: ${{ secrets.GITHUB_TOKEN }} 38 | previews: 'inertia' # 깃허브 프로젝트 API 를 위해 필수 39 | script: | 40 | console.log("======= 고유 프로젝트 아이디 " + ${{ env.PROJECT_ID }} + " 에서 데이터를 추출 합니다 ==============") 41 | 42 | // 기존 프로젝트 보드 column 읽기: 43 | try { 44 | listColumns = await github.projects.listColumns({ 45 | project_id: ${{ env.PROJECT_ID }} 46 | }) 47 | } catch(err) { 48 | throw err 49 | } 50 | 51 | console.log("총 " + listColumns.length + " column 을 다음 고유 프로젝트 아이디 에서 찾았습: " + ${{ env.PROJECT_ID }}) 52 | 53 | // column 아이디를 저장하는 리스트: 54 | listColumnIDs = [] 55 | // column 이름을 저장하는 리스트: 56 | listColumnNames = [] 57 | 58 | // column 을 룹하면서 데이터 추출: 59 | listColumns.data.forEach(function(column) { 60 | listColumnIDs.push(column.id) 61 | listColumnNames.push(column.name) 62 | }) 63 | 64 | // 포맷 안된 카드를 저장하는 리스트: 65 | listAllCardsUnformatted = [] 66 | 67 | // 카드 데이터: 68 | try { 69 | for(var i=0; i < listColumnIDs.length; ++ i) { 70 | listCards4Column = await github.projects.listCards({ 71 | column_id: listColumnIDs[i] 72 | }) 73 | listAllCardsUnformatted.push(listCards4Column.data) 74 | } 75 | 76 | } catch(err) { 77 | throw err 78 | } 79 | 80 | // 카드를 저장하는 리스트: 81 | listAllCards = [] 82 | 83 | // 새로운 리스트 : 84 | listAllCardsUnformatted.forEach(function(cardsPerColumn) { 85 | listCardsPerColumn = [] 86 | 87 | // ITERATING through all cards in this column: 88 | cardsPerColumn.forEach(function(card) { 89 | listCardsPerColumn.push(card.note ? card.note : card.content_id) 90 | }) 91 | 92 | // STORING the reversed card orders since it is ordered in an opposite way: 93 | listAllCards.push(listCardsPerColumn.reverse()) 94 | }) 95 | 96 | console.log("프로젝트 보드 안에 있는 카드들 => " + JSON.stringify(listAllCards)) 97 | 98 | // column 개수를 나타냅니다: 99 | nListColumns = listAllCards.length; 100 | 101 | console.log("\n========= 프로젝트 만들기 ==================\n") 102 | 103 | // 새로운 프로젝트 보드를 나타냅니다: 104 | var projectObj=""; 105 | 106 | // 인풋 값으로 새로운 프로젝트 보드를 만듭니다: 107 | try { 108 | projectObj = await github.projects.createForRepo({ 109 | owner: context.payload.organization.login, 110 | repo: context.payload.repository.name, 111 | name: "${{ github.event.inputs.descNewProject }} 깃허브 액션 행성 생존계획" 112 | }) 113 | } catch(err) { 114 | throw err 115 | } 116 | 117 | console.log("다음 Project ID 를 가진 새로운 프로젝트 보드가 만들어졌습니다: " + projectObj.data.id) 118 | console.log("다음, column 하고 카드를 만듭니다") 119 | 120 | // 새로운 프로젝트 보드의 카드를 만듭니다: 121 | for(var iColumn=0; iColumn < nListColumns; ++ iColumn) { 122 | try { 123 | // CREATE a new column attached to our new project ID: 124 | var columnObj = await github.projects.createColumn({ 125 | name: listColumnNames[iColumn], 126 | project_id: projectObj.data.id 127 | }) 128 | 129 | // 총 카드 수를 계산 합니다: 130 | nCardsPerColumn = listAllCards[iColumn].length 131 | 132 | // 한 column 안에 있는 카드들을 저장합니다: 133 | listOrderedCardsPerColumn = listAllCards[iColumn] 134 | 135 | console.log("이 column 의 : " + JSON.stringify(listOrderedCardsPerColumn)) 136 | 137 | // 카드를 만듭니다: 138 | for(var iCard = 0; iCard < nCardsPerColumn; ++ iCard) { 139 | try { 140 | var cardObj = await github.projects.createCard({ 141 | column_id: columnObj.data.id, 142 | note: listOrderedCardsPerColumn[iCard] 143 | }) 144 | 145 | } catch(err) { 146 | console.log("카드 만드는 중 에러: " + err); 147 | throw err; 148 | } 149 | } 150 | } catch(err) { 151 | console.log("일반 에러: " + err); 152 | throw err 153 | } 154 | } 155 | 156 | console.log("끝!") 157 | 158 | --------------------------------------------------------------------------------