From 6f984eca1cab903fc42e41d4663186c3f8930518 Mon Sep 17 00:00:00 2001 From: Andrew Bonventre Date: Thu, 24 May 2018 14:41:55 -0400 Subject: blog: clean up blog to allow usage with gcloud command + The app.yaml file needs to be in the same directory as the entry-point Go files, so those are moved from ./blog to ./ + Go files within the context article did not have the +build OMIT directive, so gcloud would view them as files that needed to be built at deploy time. Add the +build OMIT directive and use the context package instead of x/net/context. + Switch to using a service instead of version and update app.yaml to account for this. + Use 1.9 as the runtime. + Remove superfluous .gitignore Change-Id: I7c886849b912bc7f5b67cd2791cb6986d93d5cc7 Reviewed-on: https://go-review.googlesource.com/114455 Reviewed-by: Russ Cox --- .gitignore | 2 - README.md | 8 ++-- app.yaml | 5 +-- appengine.go | 28 ++++++++++++++ blog.go | 53 ++++++++++++++++++++++++++ blog/appengine.go | 28 -------------- blog/blog.go | 53 -------------------------- blog/local.go | 69 ---------------------------------- blog/local_test.go | 33 ---------------- blog/rewrite.go | 81 ---------------------------------------- content/context/google/google.go | 4 +- content/context/server/server.go | 4 +- content/context/userip/userip.go | 5 ++- local.go | 69 ++++++++++++++++++++++++++++++++++ local_test.go | 33 ++++++++++++++++ rewrite.go | 81 ++++++++++++++++++++++++++++++++++++++++ 16 files changed, 278 insertions(+), 278 deletions(-) delete mode 100644 .gitignore create mode 100644 appengine.go create mode 100644 blog.go delete mode 100644 blog/appengine.go delete mode 100644 blog/blog.go delete mode 100644 blog/local.go delete mode 100644 blog/local_test.go delete mode 100644 blog/rewrite.go create mode 100644 local.go create mode 100644 local_test.go create mode 100644 rewrite.go diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 8339fd6..0000000 --- a/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# Add no patterns to .hgignore except for files generated by the build. -last-change diff --git a/README.md b/README.md index d22821b..3305877 100644 --- a/README.md +++ b/README.md @@ -11,14 +11,12 @@ manually git clone the repository to $GOPATH/src/golang.org/x/blog. To run the blog server locally: - go build -o blog.exe ./blog - ./blog.exe +```sh +dev_appserver.py app.yaml +``` and then visit [http://localhost:8080/](http://localhost:8080) in your browser. -Note that blog.exe caches all content and you must kill it (Ctrl-C on Unix -systems) and restart it after editing any files to view changes. - ## Contributing Articles on the blog should have broad interest to the Go community, and diff --git a/app.yaml b/app.yaml index 43127be..b6edeb7 100644 --- a/app.yaml +++ b/app.yaml @@ -1,7 +1,6 @@ -application: golang-org -version: blog-test +service: blog runtime: go -api_version: go1 +api_version: go1.9 handlers: - url: /favicon.ico diff --git a/appengine.go b/appengine.go new file mode 100644 index 0000000..288247e --- /dev/null +++ b/appengine.go @@ -0,0 +1,28 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build appengine + +// This file implements an App Engine blog server. + +package main + +import ( + "net/http" + + "golang.org/x/tools/blog" +) + +func init() { + config.ContentPath = "content/" + config.TemplatePath = "template/" + s, err := blog.NewServer(config) + if err != nil { + panic(err) + } + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Strict-Transport-Security", "max-age=31536000; preload") + s.ServeHTTP(w, r) + }) +} diff --git a/blog.go b/blog.go new file mode 100644 index 0000000..9d861c1 --- /dev/null +++ b/blog.go @@ -0,0 +1,53 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Command blog is a web server for the Go blog that can run on App Engine or +// as a stand-alone HTTP server. +package main + +import ( + "net/http" + "strings" + "time" + + "golang.org/x/tools/blog" + "golang.org/x/tools/godoc/static" + + _ "golang.org/x/tools/playground" +) + +const hostname = "blog.golang.org" // default hostname for blog server + +var config = blog.Config{ + Hostname: hostname, + BaseURL: "//" + hostname, + GodocURL: "//golang.org", + HomeArticles: 5, // articles to display on the home page + FeedArticles: 10, // articles to include in Atom and JSON feeds + PlayEnabled: true, + FeedTitle: "The Go Programming Language Blog", +} + +func init() { + // Redirect "/blog/" to "/", because the menu bar link is to "/blog/" + // but we're serving from the root. + redirect := func(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, "/", http.StatusFound) + } + http.HandleFunc("/blog", redirect) + http.HandleFunc("/blog/", redirect) + http.Handle("/fonts/", http.FileServer(http.Dir("static"))) + http.Handle("/fonts.css", http.FileServer(http.Dir("static"))) + http.Handle("/lib/godoc/", http.StripPrefix("/lib/godoc/", http.HandlerFunc(staticHandler))) +} + +func staticHandler(w http.ResponseWriter, r *http.Request) { + name := r.URL.Path + b, ok := static.Files[name] + if !ok { + http.NotFound(w, r) + return + } + http.ServeContent(w, r, name, time.Time{}, strings.NewReader(b)) +} diff --git a/blog/appengine.go b/blog/appengine.go deleted file mode 100644 index 288247e..0000000 --- a/blog/appengine.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build appengine - -// This file implements an App Engine blog server. - -package main - -import ( - "net/http" - - "golang.org/x/tools/blog" -) - -func init() { - config.ContentPath = "content/" - config.TemplatePath = "template/" - s, err := blog.NewServer(config) - if err != nil { - panic(err) - } - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Strict-Transport-Security", "max-age=31536000; preload") - s.ServeHTTP(w, r) - }) -} diff --git a/blog/blog.go b/blog/blog.go deleted file mode 100644 index 9d861c1..0000000 --- a/blog/blog.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Command blog is a web server for the Go blog that can run on App Engine or -// as a stand-alone HTTP server. -package main - -import ( - "net/http" - "strings" - "time" - - "golang.org/x/tools/blog" - "golang.org/x/tools/godoc/static" - - _ "golang.org/x/tools/playground" -) - -const hostname = "blog.golang.org" // default hostname for blog server - -var config = blog.Config{ - Hostname: hostname, - BaseURL: "//" + hostname, - GodocURL: "//golang.org", - HomeArticles: 5, // articles to display on the home page - FeedArticles: 10, // articles to include in Atom and JSON feeds - PlayEnabled: true, - FeedTitle: "The Go Programming Language Blog", -} - -func init() { - // Redirect "/blog/" to "/", because the menu bar link is to "/blog/" - // but we're serving from the root. - redirect := func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, "/", http.StatusFound) - } - http.HandleFunc("/blog", redirect) - http.HandleFunc("/blog/", redirect) - http.Handle("/fonts/", http.FileServer(http.Dir("static"))) - http.Handle("/fonts.css", http.FileServer(http.Dir("static"))) - http.Handle("/lib/godoc/", http.StripPrefix("/lib/godoc/", http.HandlerFunc(staticHandler))) -} - -func staticHandler(w http.ResponseWriter, r *http.Request) { - name := r.URL.Path - b, ok := static.Files[name] - if !ok { - http.NotFound(w, r) - return - } - http.ServeContent(w, r, name, time.Time{}, strings.NewReader(b)) -} diff --git a/blog/local.go b/blog/local.go deleted file mode 100644 index 676acb5..0000000 --- a/blog/local.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !appengine - -// This file implements a stand-alone blog server. - -package main - -import ( - "flag" - "log" - "net" - "net/http" - - "golang.org/x/tools/blog" -) - -var ( - httpAddr = flag.String("http", "localhost:8080", "HTTP listen address") - contentPath = flag.String("content", "content/", "path to content files") - templatePath = flag.String("template", "template/", "path to template files") - staticPath = flag.String("static", "static/", "path to static files") - reload = flag.Bool("reload", false, "reload content on each page load") -) - -func newServer(reload bool, staticPath string, config blog.Config) (http.Handler, error) { - mux := http.NewServeMux() - if reload { - mux.HandleFunc("/", reloadingBlogServer) - } else { - s, err := blog.NewServer(config) - if err != nil { - return nil, err - } - mux.Handle("/", s) - } - fs := http.FileServer(http.Dir(staticPath)) - mux.Handle("/static/", http.StripPrefix("/static/", fs)) - return mux, nil -} - -func main() { - flag.Parse() - config.ContentPath = *contentPath - config.TemplatePath = *templatePath - mux, err := newServer(*reload, *staticPath, config) - if err != nil { - log.Fatal(err) - } - ln, err := net.Listen("tcp", *httpAddr) - if err != nil { - log.Fatal(err) - } - log.Println("Listening on addr", *httpAddr) - log.Fatal(http.Serve(ln, mux)) -} - -// reloadingBlogServer is an handler that restarts the blog server on each page -// view. Inefficient; don't enable by default. Handy when editing blog content. -func reloadingBlogServer(w http.ResponseWriter, r *http.Request) { - s, err := blog.NewServer(config) - if err != nil { - http.Error(w, err.Error(), 500) - return - } - s.ServeHTTP(w, r) -} diff --git a/blog/local_test.go b/blog/local_test.go deleted file mode 100644 index a65d815..0000000 --- a/blog/local_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// +build !appengine - -package main - -import ( - "net/http/httptest" - "strings" - "testing" - - "golang.org/x/tools/blog" -) - -func TestServer(t *testing.T) { - mux, err := newServer(false, "/static", blog.Config{ - TemplatePath: "../template", - }) - if err != nil { - t.Fatal(err) - } - r := httptest.NewRequest("GET", "/", nil) - w := httptest.NewRecorder() - mux.ServeHTTP(w, r) - if w.Code != 200 { - t.Errorf("GET /: code = %d; want 200", w.Code) - } - want := "The Go Programming Language Blog" - if !strings.Contains(w.Body.String(), want) { - t.Errorf("GET /: want to find %q, got\n\n%q", want, w.Body.String()) - } - if hdr := w.Header().Get("Content-Type"); hdr != "text/html; charset=utf-8" { - t.Errorf("GET /: want text/html content-type, got %q", hdr) - } -} diff --git a/blog/rewrite.go b/blog/rewrite.go deleted file mode 100644 index 6ab9121..0000000 --- a/blog/rewrite.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import "net/http" - -// Register HTTP handlers that redirect old blog paths to their new locations. -func init() { - for p := range urlMap { - dest := "/" + urlMap[p] - http.HandleFunc(p, func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, dest, http.StatusMovedPermanently) - }) - } -} - -var urlMap = map[string]string{ - "/2010/03/go-whats-new-in-march-2010.html": "go-whats-new-in-march-2010", - "/2010/04/json-rpc-tale-of-interfaces.html": "json-rpc-tale-of-interfaces", - "/2010/04/third-party-libraries-goprotobuf-and.html": "third-party-libraries-goprotobuf-and", - "/2010/05/go-at-io-frequently-asked-questions.html": "go-at-io-frequently-asked-questions", - "/2010/05/new-talk-and-tutorials.html": "new-talk-and-tutorials", - "/2010/05/upcoming-google-io-go-events.html": "upcoming-google-io-go-events", - "/2010/06/go-programming-session-video-from.html": "go-programming-session-video-from", - "/2010/07/gos-declaration-syntax.html": "gos-declaration-syntax", - "/2010/07/share-memory-by-communicating.html": "share-memory-by-communicating", - "/2010/08/defer-panic-and-recover.html": "defer-panic-and-recover", - "/2010/09/go-concurrency-patterns-timing-out-and.html": "go-concurrency-patterns-timing-out-and", - "/2010/09/go-wins-2010-bossie-award.html": "go-wins-2010-bossie-award", - "/2010/09/introducing-go-playground.html": "introducing-go-playground", - "/2010/10/real-go-projects-smarttwitter-and-webgo.html": "real-go-projects-smarttwitter-and-webgo", - "/2010/11/debugging-go-code-status-report.html": "debugging-go-code-status-report", - "/2010/11/go-one-year-ago-today.html": "go-one-year-ago-today", - "/2011/01/go-slices-usage-and-internals.html": "go-slices-usage-and-internals", - "/2011/01/json-and-go.html": "json-and-go", - "/2011/03/c-go-cgo.html": "c-go-cgo", - "/2011/03/go-becomes-more-stable.html": "go-becomes-more-stable", - "/2011/03/gobs-of-data.html": "gobs-of-data", - "/2011/03/godoc-documenting-go-code.html": "godoc-documenting-go-code", - "/2011/04/go-at-heroku.html": "go-at-heroku", - "/2011/04/introducing-gofix.html": "introducing-gofix", - "/2011/05/gif-decoder-exercise-in-go-interfaces.html": "gif-decoder-exercise-in-go-interfaces", - "/2011/05/go-and-google-app-engine.html": "go-and-google-app-engine", - "/2011/05/go-at-google-io-2011-videos.html": "go-at-google-io-2011-videos", - "/2011/06/first-class-functions-in-go-and-new-go.html": "first-class-functions-in-go-and-new-go", - "/2011/06/profiling-go-programs.html": "profiling-go-programs", - "/2011/06/spotlight-on-external-go-libraries.html": "spotlight-on-external-go-libraries", - "/2011/07/error-handling-and-go.html": "error-handling-and-go", - "/2011/07/go-for-app-engine-is-now-generally.html": "go-for-app-engine-is-now-generally", - "/2011/09/go-image-package.html": "go-image-package", - "/2011/09/go-imagedraw-package.html": "go-imagedraw-package", - "/2011/09/laws-of-reflection.html": "laws-of-reflection", - "/2011/09/two-go-talks-lexical-scanning-in-go-and.html": "two-go-talks-lexical-scanning-in-go-and", - "/2011/10/debugging-go-programs-with-gnu-debugger.html": "debugging-go-programs-with-gnu-debugger", - "/2011/10/go-app-engine-sdk-155-released.html": "go-app-engine-sdk-155-released", - "/2011/10/learn-go-from-your-browser.html": "learn-go-from-your-browser", - "/2011/10/preview-of-go-version-1.html": "preview-of-go-version-1", - "/2011/11/go-programming-language-turns-two.html": "go-programming-language-turns-two", - "/2011/11/writing-scalable-app-engine.html": "writing-scalable-app-engine", - "/2011/12/building-stathat-with-go.html": "building-stathat-with-go", - "/2011/12/from-zero-to-go-launching-on-google.html": "from-zero-to-go-launching-on-google", - "/2011/12/getting-to-know-go-community.html": "getting-to-know-go-community", - "/2012/03/go-version-1-is-released.html": "go-version-1-is-released", - "/2012/07/gccgo-in-gcc-471.html": "gccgo-in-gcc-471", - "/2012/07/go-videos-from-google-io-2012.html": "go-videos-from-google-io-2012", - "/2012/08/go-updates-in-app-engine-171.html": "go-updates-in-app-engine-171", - "/2012/08/organizing-go-code.html": "organizing-go-code", - "/2012/11/go-turns-three.html": "go-turns-three", - "/2013/01/concurrency-is-not-parallelism.html": "concurrency-is-not-parallelism", - "/2013/01/go-fmt-your-code.html": "go-fmt-your-code", - "/2013/01/the-app-engine-sdk-and-workspaces-gopath.html": "the-app-engine-sdk-and-workspaces-gopath", - "/2013/01/two-recent-go-talks.html": "two-recent-go-talks", - "/2013/02/getthee-to-go-meetup.html": "getthee-to-go-meetup", - "/2013/02/go-maps-in-action.html": "go-maps-in-action", - "/2013/03/two-recent-go-articles.html": "two-recent-go-articles", - "/2013/03/the-path-to-go-1.html": "the-path-to-go-1", - "/2013/05/go-11-is-released.html": "go-11-is-released.article", - "/2013/05/advanced-go-concurrency-patterns.html": "advanced-go-concurrency-patterns.article", -} diff --git a/content/context/google/google.go b/content/context/google/google.go index 740e6c5..75442d0 100644 --- a/content/context/google/google.go +++ b/content/context/google/google.go @@ -1,3 +1,5 @@ +// +build OMIT + // Package google provides a function to do Google searches using the Google Web // Search API. See https://developers.google.com/web-search/docs/ // @@ -9,11 +11,11 @@ package google import ( + "context" "encoding/json" "net/http" "golang.org/x/blog/content/context/userip" - "golang.org/x/net/context" ) // Results is an ordered list of search results. diff --git a/content/context/server/server.go b/content/context/server/server.go index 2504370..ebee9a1 100644 --- a/content/context/server/server.go +++ b/content/context/server/server.go @@ -1,3 +1,5 @@ +// +build OMIT + // The server program issues Google search requests and demonstrates the use of // the go.net Context API. It serves on port 8080. // @@ -11,6 +13,7 @@ package main import ( + "context" "html/template" "log" "net/http" @@ -18,7 +21,6 @@ import ( "golang.org/x/blog/content/context/google" "golang.org/x/blog/content/context/userip" - "golang.org/x/net/context" ) func main() { diff --git a/content/context/userip/userip.go b/content/context/userip/userip.go index 49a7522..06d3245 100644 --- a/content/context/userip/userip.go +++ b/content/context/userip/userip.go @@ -1,3 +1,5 @@ +// +build OMIT + // Package userip provides functions for extracting a user IP address from a // request and associating it with a Context. // @@ -6,11 +8,10 @@ package userip import ( + "context" "fmt" "net" "net/http" - - "golang.org/x/net/context" ) // FromRequest extracts the user IP address from req, if present. diff --git a/local.go b/local.go new file mode 100644 index 0000000..676acb5 --- /dev/null +++ b/local.go @@ -0,0 +1,69 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !appengine + +// This file implements a stand-alone blog server. + +package main + +import ( + "flag" + "log" + "net" + "net/http" + + "golang.org/x/tools/blog" +) + +var ( + httpAddr = flag.String("http", "localhost:8080", "HTTP listen address") + contentPath = flag.String("content", "content/", "path to content files") + templatePath = flag.String("template", "template/", "path to template files") + staticPath = flag.String("static", "static/", "path to static files") + reload = flag.Bool("reload", false, "reload content on each page load") +) + +func newServer(reload bool, staticPath string, config blog.Config) (http.Handler, error) { + mux := http.NewServeMux() + if reload { + mux.HandleFunc("/", reloadingBlogServer) + } else { + s, err := blog.NewServer(config) + if err != nil { + return nil, err + } + mux.Handle("/", s) + } + fs := http.FileServer(http.Dir(staticPath)) + mux.Handle("/static/", http.StripPrefix("/static/", fs)) + return mux, nil +} + +func main() { + flag.Parse() + config.ContentPath = *contentPath + config.TemplatePath = *templatePath + mux, err := newServer(*reload, *staticPath, config) + if err != nil { + log.Fatal(err) + } + ln, err := net.Listen("tcp", *httpAddr) + if err != nil { + log.Fatal(err) + } + log.Println("Listening on addr", *httpAddr) + log.Fatal(http.Serve(ln, mux)) +} + +// reloadingBlogServer is an handler that restarts the blog server on each page +// view. Inefficient; don't enable by default. Handy when editing blog content. +func reloadingBlogServer(w http.ResponseWriter, r *http.Request) { + s, err := blog.NewServer(config) + if err != nil { + http.Error(w, err.Error(), 500) + return + } + s.ServeHTTP(w, r) +} diff --git a/local_test.go b/local_test.go new file mode 100644 index 0000000..3b8de62 --- /dev/null +++ b/local_test.go @@ -0,0 +1,33 @@ +// +build !appengine + +package main + +import ( + "net/http/httptest" + "strings" + "testing" + + "golang.org/x/tools/blog" +) + +func TestServer(t *testing.T) { + mux, err := newServer(false, "/static", blog.Config{ + TemplatePath: "./template", + }) + if err != nil { + t.Fatal(err) + } + r := httptest.NewRequest("GET", "/", nil) + w := httptest.NewRecorder() + mux.ServeHTTP(w, r) + if w.Code != 200 { + t.Errorf("GET /: code = %d; want 200", w.Code) + } + want := "The Go Programming Language Blog" + if !strings.Contains(w.Body.String(), want) { + t.Errorf("GET /: want to find %q, got\n\n%q", want, w.Body.String()) + } + if hdr := w.Header().Get("Content-Type"); hdr != "text/html; charset=utf-8" { + t.Errorf("GET /: want text/html content-type, got %q", hdr) + } +} diff --git a/rewrite.go b/rewrite.go new file mode 100644 index 0000000..6ab9121 --- /dev/null +++ b/rewrite.go @@ -0,0 +1,81 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "net/http" + +// Register HTTP handlers that redirect old blog paths to their new locations. +func init() { + for p := range urlMap { + dest := "/" + urlMap[p] + http.HandleFunc(p, func(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, dest, http.StatusMovedPermanently) + }) + } +} + +var urlMap = map[string]string{ + "/2010/03/go-whats-new-in-march-2010.html": "go-whats-new-in-march-2010", + "/2010/04/json-rpc-tale-of-interfaces.html": "json-rpc-tale-of-interfaces", + "/2010/04/third-party-libraries-goprotobuf-and.html": "third-party-libraries-goprotobuf-and", + "/2010/05/go-at-io-frequently-asked-questions.html": "go-at-io-frequently-asked-questions", + "/2010/05/new-talk-and-tutorials.html": "new-talk-and-tutorials", + "/2010/05/upcoming-google-io-go-events.html": "upcoming-google-io-go-events", + "/2010/06/go-programming-session-video-from.html": "go-programming-session-video-from", + "/2010/07/gos-declaration-syntax.html": "gos-declaration-syntax", + "/2010/07/share-memory-by-communicating.html": "share-memory-by-communicating", + "/2010/08/defer-panic-and-recover.html": "defer-panic-and-recover", + "/2010/09/go-concurrency-patterns-timing-out-and.html": "go-concurrency-patterns-timing-out-and", + "/2010/09/go-wins-2010-bossie-award.html": "go-wins-2010-bossie-award", + "/2010/09/introducing-go-playground.html": "introducing-go-playground", + "/2010/10/real-go-projects-smarttwitter-and-webgo.html": "real-go-projects-smarttwitter-and-webgo", + "/2010/11/debugging-go-code-status-report.html": "debugging-go-code-status-report", + "/2010/11/go-one-year-ago-today.html": "go-one-year-ago-today", + "/2011/01/go-slices-usage-and-internals.html": "go-slices-usage-and-internals", + "/2011/01/json-and-go.html": "json-and-go", + "/2011/03/c-go-cgo.html": "c-go-cgo", + "/2011/03/go-becomes-more-stable.html": "go-becomes-more-stable", + "/2011/03/gobs-of-data.html": "gobs-of-data", + "/2011/03/godoc-documenting-go-code.html": "godoc-documenting-go-code", + "/2011/04/go-at-heroku.html": "go-at-heroku", + "/2011/04/introducing-gofix.html": "introducing-gofix", + "/2011/05/gif-decoder-exercise-in-go-interfaces.html": "gif-decoder-exercise-in-go-interfaces", + "/2011/05/go-and-google-app-engine.html": "go-and-google-app-engine", + "/2011/05/go-at-google-io-2011-videos.html": "go-at-google-io-2011-videos", + "/2011/06/first-class-functions-in-go-and-new-go.html": "first-class-functions-in-go-and-new-go", + "/2011/06/profiling-go-programs.html": "profiling-go-programs", + "/2011/06/spotlight-on-external-go-libraries.html": "spotlight-on-external-go-libraries", + "/2011/07/error-handling-and-go.html": "error-handling-and-go", + "/2011/07/go-for-app-engine-is-now-generally.html": "go-for-app-engine-is-now-generally", + "/2011/09/go-image-package.html": "go-image-package", + "/2011/09/go-imagedraw-package.html": "go-imagedraw-package", + "/2011/09/laws-of-reflection.html": "laws-of-reflection", + "/2011/09/two-go-talks-lexical-scanning-in-go-and.html": "two-go-talks-lexical-scanning-in-go-and", + "/2011/10/debugging-go-programs-with-gnu-debugger.html": "debugging-go-programs-with-gnu-debugger", + "/2011/10/go-app-engine-sdk-155-released.html": "go-app-engine-sdk-155-released", + "/2011/10/learn-go-from-your-browser.html": "learn-go-from-your-browser", + "/2011/10/preview-of-go-version-1.html": "preview-of-go-version-1", + "/2011/11/go-programming-language-turns-two.html": "go-programming-language-turns-two", + "/2011/11/writing-scalable-app-engine.html": "writing-scalable-app-engine", + "/2011/12/building-stathat-with-go.html": "building-stathat-with-go", + "/2011/12/from-zero-to-go-launching-on-google.html": "from-zero-to-go-launching-on-google", + "/2011/12/getting-to-know-go-community.html": "getting-to-know-go-community", + "/2012/03/go-version-1-is-released.html": "go-version-1-is-released", + "/2012/07/gccgo-in-gcc-471.html": "gccgo-in-gcc-471", + "/2012/07/go-videos-from-google-io-2012.html": "go-videos-from-google-io-2012", + "/2012/08/go-updates-in-app-engine-171.html": "go-updates-in-app-engine-171", + "/2012/08/organizing-go-code.html": "organizing-go-code", + "/2012/11/go-turns-three.html": "go-turns-three", + "/2013/01/concurrency-is-not-parallelism.html": "concurrency-is-not-parallelism", + "/2013/01/go-fmt-your-code.html": "go-fmt-your-code", + "/2013/01/the-app-engine-sdk-and-workspaces-gopath.html": "the-app-engine-sdk-and-workspaces-gopath", + "/2013/01/two-recent-go-talks.html": "two-recent-go-talks", + "/2013/02/getthee-to-go-meetup.html": "getthee-to-go-meetup", + "/2013/02/go-maps-in-action.html": "go-maps-in-action", + "/2013/03/two-recent-go-articles.html": "two-recent-go-articles", + "/2013/03/the-path-to-go-1.html": "the-path-to-go-1", + "/2013/05/go-11-is-released.html": "go-11-is-released.article", + "/2013/05/advanced-go-concurrency-patterns.html": "advanced-go-concurrency-patterns.article", +} -- cgit v1.2.3